mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-04-23 04:25:21 +08:00
244 lines
7.4 KiB
Java
244 lines
7.4 KiB
Java
package com.jsowell.wxpay.utils;
|
||
|
||
import com.fasterxml.jackson.databind.JsonNode;
|
||
import com.jsowell.wxpay.common.WeChatPayParameter;
|
||
|
||
import java.io.ByteArrayInputStream;
|
||
import java.io.IOException;
|
||
import java.net.URL;
|
||
import java.nio.file.Files;
|
||
import java.nio.file.Paths;
|
||
import java.security.InvalidKeyException;
|
||
import java.security.KeyFactory;
|
||
import java.security.NoSuchAlgorithmException;
|
||
import java.security.PrivateKey;
|
||
import java.security.Signature;
|
||
import java.security.SignatureException;
|
||
import java.security.cert.CertificateFactory;
|
||
import java.security.cert.X509Certificate;
|
||
import java.security.spec.InvalidKeySpecException;
|
||
import java.security.spec.PKCS8EncodedKeySpec;
|
||
import java.util.Base64;
|
||
import java.util.HashMap;
|
||
import java.util.Map;
|
||
import java.util.UUID;
|
||
|
||
|
||
/**
|
||
* 参考官网 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml
|
||
*/
|
||
public class WechatPayUtils {
|
||
/**
|
||
* 获取私钥。
|
||
*
|
||
* @param filename 私钥文件路径 (required)
|
||
* @return 私钥对象
|
||
*/
|
||
public static PrivateKey getPrivateKey(String filename) throws IOException {
|
||
// System.out.println("filename:" + filename);
|
||
String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
|
||
try {
|
||
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
|
||
.replace("-----END PRIVATE KEY-----", "")
|
||
.replaceAll("\\s+", "");
|
||
|
||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||
return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
|
||
} catch (NoSuchAlgorithmException e) {
|
||
throw new RuntimeException("当前Java环境不支持RSA", e);
|
||
} catch (InvalidKeySpecException e) {
|
||
throw new RuntimeException("无效的密钥格式");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成token 也就是生成签名
|
||
*
|
||
* @param method
|
||
* @param url
|
||
* @param body
|
||
* @return
|
||
* @throws Exception
|
||
*/
|
||
public static String getToken(String method, URL url, String body) throws Exception {
|
||
String nonceStr = getNonceStr();
|
||
long timestamp = System.currentTimeMillis() / 1000;
|
||
String message = buildMessage(method, url, timestamp, nonceStr, body);
|
||
String signature = sign(message.getBytes("utf-8"));
|
||
|
||
return "WECHATPAY2-SHA256-RSA2048 " + "mchid=\"" + WeChatPayParameter.mchId + "\","
|
||
+ "nonce_str=\"" + nonceStr + "\","
|
||
+ "timestamp=\"" + timestamp + "\","
|
||
+ "serial_no=\"" + WeChatPayParameter.mchSerialNo + "\","
|
||
+ "signature=\"" + signature + "\"";
|
||
}
|
||
|
||
/**
|
||
* 生成token
|
||
*
|
||
* @param method
|
||
* @param url
|
||
* @param body
|
||
* @return
|
||
* @throws Exception
|
||
*/
|
||
public static Map<String, Object> getTokenWeixin(String method, URL url, String body, String prepay_id) throws Exception {
|
||
String nonceStr = getNonceStr();
|
||
long timestamp = System.currentTimeMillis() / 1000;
|
||
String message = buildMessage(method, url, timestamp, nonceStr, body);
|
||
String signature = sign(message.getBytes("utf-8"));
|
||
|
||
Map<String, Object> map = new HashMap<>();
|
||
map.put("timeStamp", String.valueOf(timestamp));
|
||
map.put("nonceStr", nonceStr);
|
||
map.put("package", "prepay_id=" + prepay_id);
|
||
map.put("signType", "RSA");
|
||
map.put("paySign", signature);
|
||
|
||
return map;
|
||
}
|
||
|
||
|
||
/**
|
||
* 生成签名
|
||
*
|
||
* @param message
|
||
* @return
|
||
* @throws Exception
|
||
*/
|
||
public static String sign(byte[] message) throws Exception {
|
||
Signature sign = Signature.getInstance("SHA256withRSA");
|
||
sign.initSign(WeChatPayParameter.privateKey);
|
||
sign.update(message);
|
||
return Base64.getEncoder().encodeToString(sign.sign());
|
||
}
|
||
|
||
/**
|
||
* 生成签名串
|
||
*
|
||
* @param method
|
||
* @param url
|
||
* @param timestamp
|
||
* @param nonceStr
|
||
* @param body
|
||
* @return
|
||
*/
|
||
public static String buildMessage(String method, URL url, long timestamp, String nonceStr, String body) {
|
||
String canonicalUrl = url.getPath();
|
||
if (url.getQuery() != null) {
|
||
canonicalUrl += "?" + url.getQuery();
|
||
}
|
||
return method + "\n"
|
||
+ canonicalUrl + "\n"
|
||
+ timestamp + "\n"
|
||
+ nonceStr + "\n"
|
||
+ body + "\n";
|
||
}
|
||
|
||
/**
|
||
* 生成随机数
|
||
*
|
||
* @return
|
||
*/
|
||
public static String getNonceStr() {
|
||
return UUID.randomUUID().toString()
|
||
.replaceAll("-", "")
|
||
.substring(0, 32);
|
||
}
|
||
|
||
/**
|
||
* 获取平台证书
|
||
*
|
||
* @return
|
||
*/
|
||
public static Map<String, X509Certificate> refreshCertificate() throws Exception {
|
||
Map<String, X509Certificate> certificateMap = new HashMap();
|
||
// 1: 执行get请求
|
||
JsonNode jsonNode = HttpUtils.doGet(WeChatPayParameter.certificatesUrl);
|
||
// 2: 获取平台验证的相关参数信息
|
||
JsonNode data = jsonNode.get("data");
|
||
if (data != null) {
|
||
for (int i = 0; i < data.size(); i++) {
|
||
JsonNode encrypt_certificate = data.get(i).get("encrypt_certificate");
|
||
//对关键信息进行解密
|
||
AesUtil aesUtil = new AesUtil(WeChatPayParameter.v3Key.getBytes());
|
||
String associated_data = encrypt_certificate.get("associated_data").toString().replaceAll("\"", "");
|
||
String nonce = encrypt_certificate.get("nonce").toString().replaceAll("\"", "");
|
||
String ciphertext = encrypt_certificate.get("ciphertext").toString().replaceAll("\"", "");
|
||
//证书内容
|
||
String certStr = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
|
||
//证书内容转成证书对象
|
||
CertificateFactory cf = CertificateFactory.getInstance("X509");
|
||
X509Certificate x509Cert = (X509Certificate) cf.generateCertificate(
|
||
new ByteArrayInputStream(certStr.getBytes("utf-8"))
|
||
);
|
||
String serial_no = data.get(i).get("serial_no").toString().replaceAll("\"", "");
|
||
certificateMap.put(serial_no, x509Cert);
|
||
}
|
||
}
|
||
return certificateMap;
|
||
}
|
||
|
||
/**
|
||
* 验证签名
|
||
*
|
||
* @param certificate
|
||
* @param message
|
||
* @param signature
|
||
* @return
|
||
*/
|
||
public static boolean verify(X509Certificate certificate, byte[] message, String signature) {
|
||
try {
|
||
Signature sign = Signature.getInstance("SHA256withRSA");
|
||
sign.initVerify(certificate);
|
||
sign.update(message);
|
||
return sign.verify(Base64.getDecoder().decode(signature));
|
||
} catch (NoSuchAlgorithmException e) {
|
||
throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
|
||
} catch (SignatureException e) {
|
||
throw new RuntimeException("签名验证过程发生了错误", e);
|
||
} catch (InvalidKeyException e) {
|
||
throw new RuntimeException("无效的证书", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 参考网站 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml
|
||
*
|
||
* @param appId
|
||
* @param prepay_id
|
||
* @return
|
||
* @throws IOException
|
||
* @throws SignatureException
|
||
* @throws NoSuchAlgorithmException
|
||
* @throws InvalidKeyException
|
||
*/
|
||
public static HashMap<String, Object> getTokenWeixin(String appId, String prepay_id) throws Exception {
|
||
// 获取随机字符串
|
||
String nonceStr = getNonceStr();
|
||
// 获取微信小程序支付package
|
||
String packagestr = "prepay_id=" + prepay_id;
|
||
long timestamp = System.currentTimeMillis() / 1000;
|
||
//签名,使用字段appId、timeStamp、nonceStr、package计算得出的签名值
|
||
String message = buildMessageTwo(appId, timestamp, nonceStr, packagestr);
|
||
//获取对应的签名
|
||
String signature = sign(message.getBytes("utf-8"));
|
||
// 组装返回
|
||
HashMap<String, Object> map = new HashMap<>();
|
||
map.put("timeStamp", String.valueOf(timestamp));
|
||
map.put("nonceStr", nonceStr);
|
||
map.put("package", packagestr);
|
||
map.put("signType", "RSA");
|
||
map.put("paySign", signature);
|
||
return map;
|
||
}
|
||
|
||
private static String buildMessageTwo(String appId, long timestamp, String nonceStr, String packag) {
|
||
return appId + "\n"
|
||
+ timestamp + "\n"
|
||
+ nonceStr + "\n"
|
||
+ packag + "\n";
|
||
}
|
||
|
||
}
|