下面的评论有很多想看我的postSSL的源码,这里奉上
public static String postSSL(String url, String data, String certPath, String certPass) {
String DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36";
HttpsURLConnection conn = null;
OutputStream out = null;
InputStream inputStream = null;
BufferedReader reader = null;
try {
KeyStore clientStore = KeyStore.getInstance("PKCS12");
clientStore.load(new FileInputStream(certPath), certPass.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(clientStore, certPass.toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
SSLContext sslContext = SSLContext.getInstance("TLSv1");
sslContext.init(kms, null, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
URL _url = new URL(url);
conn = (HttpsURLConnection) _url.openConnection();
conn.setConnectTimeout(25000);
conn.setReadTimeout(25000);
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
conn.setRequestProperty("User-Agent", DEFAULT_USER_AGENT);
conn.connect();
out = conn.getOutputStream();
out.write(data.getBytes(Charset.forName("UTF-8")));
out.flush();
inputStream = conn.getInputStream();
reader = new BufferedReader(new InputStreamReader(inputStream,Charset.forName("UTF-8")));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null){
sb.append(line).append("\n");
}
return sb.toString();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(reader);
IOUtils.closeQuietly(inputStream);
if (conn != null) {
conn.disconnect();
}
}
}
----------------------------------分割线------------------------------------------------------------
首先是退款,根据接口传参数就ok,这里直接上代码,里面有退款以后回调参数中的req_info解密方法。
package com.refund;
/**
* Author: 一个退款的demo
* Time: 2018/10/29
* 介绍:退款接口
*/
public class CompanyRefund extends Controller {
private static final Logger log = LoggerFactory.getLogger(CompanyRefund.class);
private static final String REFUND_PAY = "https://api.mch.weixin.qq.com/secapi/pay/refund";
private final String Key = PropKit.use("cfg.properties").get("key");
private final String Mchid = PropKit.use("cfg.properties").get("Mchid");
private final String AppId = PropKit.use("cfg.properties").get("wechat.appid");
private final String care=PathKit.getWebRootPath() + "/apiclient_cert.p12";
private static final String ALGORITHM = "AES";
private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding";
private static SecretKeySpec key = new SecretKeySpec(MD5Util.MD5Encode(PropKit.use("cfg.properties").get("key"), "UTF-8").toLowerCase().getBytes(), ALGORITHM);
static String str;
static String time;
//退款需要的参数 用户的openid 订单总金额 退款金额 退款原因
public void refundment() {
HttpServletRequest request = getRequest();
//当前请求地址
String basePath = request.getScheme() + "://" + request.getServerName();
String notify_url = basePath + "/refund/callback.html";
Map refund = new HashMap();
refund.put("appid", AppId);//公众账号ID
refund.put("mch_id", Mchid);//商户号
refund.put("nonce_str",getRandomStringByLength(32));//随机字符串
refund.put("out_trade_no","B201810301432115989734");//商户订单号
refund.put("out_refund_no","B201810301432115989734");//商户退款订单号
refund.put("total_fee","1");//订单金额
refund.put("refund_desc","就是想退款");//退款原因
refund.put("refund_fee","1");//退款金额
refund.put("notify_url", notify_url);//微信回调通知地址,可以使用自己定义也可以使用回调到指定的位置这里使用的是自定义可以在回调中添加一些功能
String sign = PaymentKit.createSign(refund, Key + "");//map+密钥
refund.put("sign",sign);//签名
//退款方法,这个map也是退款结果返回的map只可用于作为判断是否正确的
Map result = refunds(refund,Key,care);//map
//状态码校验格式是否正确,Success是正确的
if(result.get("return_code").equals("SUCCESS")){
//退款的结果这是一个map
// System.out.println(result);
// String xmlResult= PaymentKit.toXml(result);//map转xml
// System.out.println(xmlResult);
if("SUCCESS".equals(result.get("result_code"))){
// Map packageParams = new HashMap<>();
// packageParams.put("result_code",result.get("result_code"));//业务返回结果
// packageParams.put("appid", result.get("appid"));//公众账号ID
// packageParams.put("mch_id",result.get("mch_id"));//商户号
// packageParams.put("nonce_str",result.get("nonce_str"));//随机字符串
// packageParams.put("sign",result.get("sign"));//签名
// packageParams.put("transaction_id",result.get("transaction_id"));//微信订单号
// packageParams.put("out_trade_no",result.get("out_trade_no"));//商户订单号
// packageParams.put("out_refund_no",result.get("out_refund_no"));//商户退款订单号
// packageParams.put("refund_id",result.get("refund_id"));//微信退款订单号
// packageParams.put("refund_fee",result.get("refund_fee"));//退款金额
// packageParams.put("total_fee",result.get("total_fee"));//标价金额
// packageParams.put("cash_fee",result.get("cash_fee"));//现金支付金额
renderJson(JsonUtils.toJson(result));
//退款申请成功
System.out.println("退款成功");
}else if("FAIL".equals(result.get("result_code"))){
renderText(result.get("result_code"));
return;
}
}else if("FAIL".equals(result.get("return_code"))){
String return_msg = result.get("return_msg");//返回信息
//失败
System.out.println(return_msg);
renderText(return_msg);
return ;
}
}
@Before(Tx.class)
@ClearShiro
public void callback() {
String xmlMsg = HttpKit.readData(getRequest());
Map params=new HashMap<>();
Document result ;
try {
result = DocumentHelper.parseText(xmlMsg);
Element rootElt = result.getRootElement(); // 获取根节点
List list = rootElt.elements();//获取根节点下所有节点
for (Element element : list) { //遍历节点
params.put(element.getName(), element.getText()); //节点的name为map的key,text为map的value
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(params);
String return_code=params.get("return_code");
String appid=params.get("appid");
String mch_id=params.get("mch_id");
String req_info=params.get("req_info");//回调加密字段
try {
System.out.println(decryptData(req_info));//这里就是解密以后的req_info
} catch (Exception e) {
e.printStackTrace();
}
String keyMd5 = HashKit.md5(Key).toLowerCase();
renderJson();
}
public static String encryptData(String data) throws Exception {
Security.addProvider(new BouncyCastleProvider());
// 创建密码器
Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING, "BC");
// 初始化
cipher.init(Cipher.ENCRYPT_MODE, key);
return Base64Util.encode(cipher.doFinal(data.getBytes()));
}
public static String decryptData(String base64Data) throws Exception {
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING, "BC");
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(Base64Util.decode(base64Data)));
}
/**
* 申请退款,内部添加了随机字符串nonce_str和签名sign
* @param params 参数map,内部添加了随机字符串nonce_str和签名sign
* @param paternerKey 商户密钥
* @param certPath 证书文件目录
* @return map
*/
public static Map refunds(Map params, String paternerKey, String certPath) {
params.put("nonce_str", System.currentTimeMillis() + "");
String sign = PaymentKit.createSign(params, paternerKey);
params.put("sign", sign);
String partner = params.get("mch_id");
String xmlStr = HttpUtils.postSSL(REFUND_PAY, PaymentKit.toXml(params), certPath, partner);
Map map = new HashMap();
Document result = null;
try {
result = DocumentHelper.parseText(xmlStr);
Element rootElt = result.getRootElement(); // 获取根节点
List list = rootElt.elements();//获取根节点下所有节点
for (Element element : list) { //遍历节点
map.put(element.getName(), element.getText()); //节点的name为map的key,text为map的value
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
//生成随机字符串
public static String getRandomStringByLength(int length) {
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
public String time(){
Long s= new Date().getTime();
Random random=new Random();
int result=random.nextInt(10);
return s.toString();
}
}
上面是debug退款方式,已经成功下面给回调函数中的成功效果
ok,搞定,解码的方式下面写出来啦
这里用的框架是jfinal,所以很多插件也是自带的那种,这里不一一解释,需要的话去看一下jfinal的文档就可以啦,接下来是解密的几个插件,首先需要
a、添加maven依赖
org.bouncycastle
bcprov-jdk15on
1.47
b、替换jar包
JAVA运行环境默认不允许256位密钥的AES加解密,解决方法就是修改策略文件
在官方网站下载JCE无限制权限策略文件
JDK7版本JCE下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
JDK8版本JCE下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
实践:
以JDK8为例,系统为WIN10,替换上述security文件夹下\policy\limited文件夹和\policy\unlimited文件夹里面的local_policy.jar和US_export_policy.jar这两个文件。
若是在服务器上,则只有在security目录下有local_policy.jar和US_export_policy.jar,替换即可
感谢简书的推荐
链接:https://www.jianshu.com/p/6d3259e88b80
还有就是几个插件
Base64Util
package com.tpxinxi.petsuu.mini.util;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
/**
* Author: 123
* Time: 2018/10/30
* 介绍:
*/
public class Base64Util {
private static final char S_BASE64CHAR[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
private static final byte S_DECODETABLE[];
static {
S_DECODETABLE = new byte[128];
for (int i = 0; i < S_DECODETABLE.length; i++)
S_DECODETABLE[i] = 127;
for (int i = 0; i < S_BASE64CHAR.length; i++)
S_DECODETABLE[S_BASE64CHAR[i]] = (byte) i;
}
/**
* @param ibuf
* @param obuf
* @param wp
* @return
*/
private static int decode0(char ibuf[], byte obuf[], int wp) {
int outlen = 3;
if (ibuf[3] == '=')
outlen = 2;
if (ibuf[2] == '=')
outlen = 1;
int b0 = S_DECODETABLE[ibuf[0]];
int b1 = S_DECODETABLE[ibuf[1]];
int b2 = S_DECODETABLE[ibuf[2]];
int b3 = S_DECODETABLE[ibuf[3]];
switch (outlen) {
case 1: // '\001'
obuf[wp] = (byte) (b0 << 2 & 252 | b1 >> 4 & 3);
return 1;
case 2: // '\002'
obuf[wp++] = (byte) (b0 << 2 & 252 | b1 >> 4 & 3);
obuf[wp] = (byte) (b1 << 4 & 240 | b2 >> 2 & 15);
return 2;
case 3: // '\003'
obuf[wp++] = (byte) (b0 << 2 & 252 | b1 >> 4 & 3);
obuf[wp++] = (byte) (b1 << 4 & 240 | b2 >> 2 & 15);
obuf[wp] = (byte) (b2 << 6 & 192 | b3 & 63);
return 3;
}
throw new RuntimeException("Internal error");
}
/**
* @param data
* @param off
* @param len
* @return
*/
public static byte[] decode(char data[], int off, int len) {
char ibuf[] = new char[4];
int ibufcount = 0;
byte obuf[] = new byte[(len / 4) * 3 + 3];
int obufcount = 0;
for (int i = off; i < off + len; i++) {
char ch = data[i];
if (ch != '=' && (ch >= S_DECODETABLE.length || S_DECODETABLE[ch] == 127))
continue;
ibuf[ibufcount++] = ch;
if (ibufcount == ibuf.length) {
ibufcount = 0;
obufcount += decode0(ibuf, obuf, obufcount);
}
}
if (obufcount == obuf.length) {
return obuf;
} else {
byte ret[] = new byte[obufcount];
System.arraycopy(obuf, 0, ret, 0, obufcount);
return ret;
}
}
/**
* @param data
* @return
*/
public static byte[] decode(String data) {
char ibuf[] = new char[4];
int ibufcount = 0;
byte obuf[] = new byte[(data.length() / 4) * 3 + 3];
int obufcount = 0;
for (int i = 0; i < data.length(); i++) {
char ch = data.charAt(i);
if (ch != '=' && (ch >= S_DECODETABLE.length || S_DECODETABLE[ch] == 127))
continue;
ibuf[ibufcount++] = ch;
if (ibufcount == ibuf.length) {
ibufcount = 0;
obufcount += decode0(ibuf, obuf, obufcount);
}
}
if (obufcount == obuf.length) {
return obuf;
} else {
byte ret[] = new byte[obufcount];
System.arraycopy(obuf, 0, ret, 0, obufcount);
return ret;
}
}
/**
* @param data
* @param off
* @param len
* @param ostream
* @throws IOException
*/
public static void decode(char data[], int off, int len, OutputStream ostream) throws IOException {
char ibuf[] = new char[4];
int ibufcount = 0;
byte obuf[] = new byte[3];
for (int i = off; i < off + len; i++) {
char ch = data[i];
if (ch != '=' && (ch >= S_DECODETABLE.length || S_DECODETABLE[ch] == 127))
continue;
ibuf[ibufcount++] = ch;
if (ibufcount == ibuf.length) {
ibufcount = 0;
int obufcount = decode0(ibuf, obuf, 0);
ostream.write(obuf, 0, obufcount);
}
}
}
/**
* @param data
* @param ostream
* @throws IOException
*/
public static void decode(String data, OutputStream ostream) throws IOException {
char ibuf[] = new char[4];
int ibufcount = 0;
byte obuf[] = new byte[3];
for (int i = 0; i < data.length(); i++) {
char ch = data.charAt(i);
if (ch != '=' && (ch >= S_DECODETABLE.length || S_DECODETABLE[ch] == 127))
continue;
ibuf[ibufcount++] = ch;
if (ibufcount == ibuf.length) {
ibufcount = 0;
int obufcount = decode0(ibuf, obuf, 0);
ostream.write(obuf, 0, obufcount);
}
}
}
/**
* @param data
* @return
*/
public static String encode(byte data[]) {
return encode(data, 0, data.length);
}
/**
* @param data
* @param off
* @param len
* @return
*/
public static String encode(byte data[], int off, int len) {
if (len <= 0)
return "";
char out[] = new char[(len / 3) * 4 + 4];
int rindex = off;
int windex = 0;
int rest;
for (rest = len - off; rest >= 3; rest -= 3) {
int i = ((data[rindex] & 255) << 16) + ((data[rindex + 1] & 255) << 8) + (data[rindex + 2] & 255);
out[windex++] = S_BASE64CHAR[i >> 18];
out[windex++] = S_BASE64CHAR[i >> 12 & 63];
out[windex++] = S_BASE64CHAR[i >> 6 & 63];
out[windex++] = S_BASE64CHAR[i & 63];
rindex += 3;
}
if (rest == 1) {
int i = data[rindex] & 255;
out[windex++] = S_BASE64CHAR[i >> 2];
out[windex++] = S_BASE64CHAR[i << 4 & 63];
out[windex++] = '=';
out[windex++] = '=';
} else if (rest == 2) {
int i = ((data[rindex] & 255) << 8) + (data[rindex + 1] & 255);
out[windex++] = S_BASE64CHAR[i >> 10];
out[windex++] = S_BASE64CHAR[i >> 4 & 63];
out[windex++] = S_BASE64CHAR[i << 2 & 63];
out[windex++] = '=';
}
return new String(out, 0, windex);
}
/**
* @param data
* @param off
* @param len
* @param ostream
* @throws IOException
*/
public static void encode(byte data[], int off, int len, OutputStream ostream) throws IOException {
if (len <= 0)
return;
byte out[] = new byte[4];
int rindex = off;
int rest;
for (rest = len - off; rest >= 3; rest -= 3) {
int i = ((data[rindex] & 255) << 16) + ((data[rindex + 1] & 255) << 8) + (data[rindex + 2] & 255);
out[0] = (byte) S_BASE64CHAR[i >> 18];
out[1] = (byte) S_BASE64CHAR[i >> 12 & 63];
out[2] = (byte) S_BASE64CHAR[i >> 6 & 63];
out[3] = (byte) S_BASE64CHAR[i & 63];
ostream.write(out, 0, 4);
rindex += 3;
}
if (rest == 1) {
int i = data[rindex] & 255;
out[0] = (byte) S_BASE64CHAR[i >> 2];
out[1] = (byte) S_BASE64CHAR[i << 4 & 63];
out[2] = 61;
out[3] = 61;
ostream.write(out, 0, 4);
} else if (rest == 2) {
int i = ((data[rindex] & 255) << 8) + (data[rindex + 1] & 255);
out[0] = (byte) S_BASE64CHAR[i >> 10];
out[1] = (byte) S_BASE64CHAR[i >> 4 & 63];
out[2] = (byte) S_BASE64CHAR[i << 2 & 63];
out[3] = 61;
ostream.write(out, 0, 4);
}
}
/**
* @param data
* @param off
* @param len
* @param writer
* @throws IOException
*/
public static void encode(byte data[], int off, int len, Writer writer) throws IOException {
if (len <= 0)
return;
char out[] = new char[4];
int rindex = off;
int rest = len - off;
int output = 0;
do {
if (rest < 3)
break;
int i = ((data[rindex] & 255) << 16) + ((data[rindex + 1] & 255) << 8) + (data[rindex + 2] & 255);
out[0] = S_BASE64CHAR[i >> 18];
out[1] = S_BASE64CHAR[i >> 12 & 63];
out[2] = S_BASE64CHAR[i >> 6 & 63];
out[3] = S_BASE64CHAR[i & 63];
writer.write(out, 0, 4);
rindex += 3;
rest -= 3;
if ((output += 4) % 76 == 0)
writer.write("\n");
}
while (true);
if (rest == 1) {
int i = data[rindex] & 255;
out[0] = S_BASE64CHAR[i >> 2];
out[1] = S_BASE64CHAR[i << 4 & 63];
out[2] = '=';
out[3] = '=';
writer.write(out, 0, 4);
} else if (rest == 2) {
int i = ((data[rindex] & 255) << 8) + (data[rindex + 1] & 255);
out[0] = S_BASE64CHAR[i >> 10];
out[1] = S_BASE64CHAR[i >> 4 & 63];
out[2] = S_BASE64CHAR[i << 2 & 63];
out[3] = '=';
writer.write(out, 0, 4);
}
}
}
Md5Util
package com.tpxinxi.petsuu.mini.util;
import java.security.MessageDigest;
/**
* Author:
* Time: 2018/10/30
* 介绍:
*/
public class MD5Util {
public final static String MD5(String s) {
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
try {
byte[] btInput = s.getBytes();
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获得密文
byte[] md = mdInst.digest();
// 把密文转换成十六进制的字符串形式
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
}