package com.project.common.wxv3;
|
|
|
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
|
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
|
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
|
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
|
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
|
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
|
import lombok.SneakyThrows;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
|
import javax.servlet.http.HttpServletRequest;
|
import java.io.ByteArrayInputStream;
|
import java.io.IOException;
|
import java.io.UnsupportedEncodingException;
|
import java.nio.charset.StandardCharsets;
|
import java.nio.file.Files;
|
import java.nio.file.Paths;
|
import java.security.*;
|
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.PKCS8EncodedKeySpec;
|
import java.util.Base64;
|
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.stream.Collectors;
|
import java.util.stream.Stream;
|
|
public class WXPaySignatureCertificateUtil {
|
|
|
|
|
/**
|
* 证书验证
|
* 自动更新的签名验证器
|
*/
|
public static CloseableHttpClient checkSign() throws IOException {
|
//验签
|
CloseableHttpClient httpClient = null;
|
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(WxV3PayConfig.privateKey.getBytes("utf-8")));
|
httpClient = WechatPayHttpClientBuilder.create()
|
.withMerchant(WxV3PayConfig.Mch_ID, WxV3PayConfig.mchSerialNo, merchantPrivateKey)
|
.withValidator(new WechatPay2Validator(WXPaySignatureCertificateUtil.getVerifier(WxV3PayConfig.mchSerialNo)))
|
.build();
|
|
return httpClient;
|
}
|
|
|
|
|
/**
|
* 保存微信平台证书
|
*/
|
private static final ConcurrentHashMap<String, AutoUpdateCertificatesVerifier> verifierMap = new ConcurrentHashMap<>();
|
|
/**
|
* 功能描述:获取平台证书,自动更新
|
* 注意:这个方法内置了平台证书的获取和返回值解密
|
*/
|
static AutoUpdateCertificatesVerifier getVerifier(String mchSerialNo) {
|
AutoUpdateCertificatesVerifier verifier = null;
|
if (verifierMap.isEmpty() || !verifierMap.containsKey(mchSerialNo)) {
|
verifierMap.clear();
|
try {
|
//传入证书
|
PrivateKey privateKey = getPrivateKey();
|
//刷新
|
PrivateKeySigner signer = new PrivateKeySigner(mchSerialNo, privateKey);
|
WechatPay2Credentials credentials = new WechatPay2Credentials(WxV3PayConfig.Mch_ID, signer);
|
verifier = new AutoUpdateCertificatesVerifier(credentials
|
, WxV3PayConfig.apiV3Key.getBytes("utf-8"));
|
verifierMap.put(verifier.getValidCertificate().getSerialNumber()+"", verifier);
|
} catch (UnsupportedEncodingException e) {
|
e.printStackTrace();
|
} catch (IOException e) {
|
throw new RuntimeException(e);
|
}
|
} else {
|
verifier = verifierMap.get(mchSerialNo);
|
}
|
return verifier;
|
}
|
|
|
|
/**
|
* 生成带签名支付信息
|
*
|
* @param timestamp 时间戳
|
* @param nonceStr 随机数
|
* @param prepayId 预付单
|
* @return 支付信息
|
* @throws Exception
|
*/
|
public static String appPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
|
//上传私钥
|
PrivateKey privateKey = getPrivateKey();
|
String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, prepayId)
|
.collect(Collectors.joining("\n", "", "\n"));
|
Signature sign = Signature.getInstance("SHA256withRSA");
|
sign.initSign(privateKey);
|
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
|
return Base64.getEncoder().encodeToString(sign.sign());
|
}
|
|
/**
|
* 生成带签名支付信息
|
*
|
* @param timestamp 时间戳
|
* @param nonceStr 随机数
|
* @param prepayId 预付单
|
* @return 支付信息
|
* @throws Exception
|
*/
|
public static String jsApiPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
|
//上传私钥
|
PrivateKey privateKey = getPrivateKey();
|
String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, "prepay_id="+prepayId)
|
.collect(Collectors.joining("\n", "", "\n"));
|
Signature sign = Signature.getInstance("SHA256withRSA");
|
sign.initSign(privateKey);
|
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
|
return Base64.getEncoder().encodeToString(sign.sign());
|
}
|
|
|
/**
|
* 获取私钥。
|
* 证书路径 本地使用如: D:\\微信平台证书工具\\7.9\\apiclient_key.pem
|
* 证书路径 线上使用如: /usr/apiclient_key.pem
|
* String filename 私钥文件路径 (required)
|
* @return 私钥对象
|
*/
|
public static PrivateKey getPrivateKey() throws IOException {
|
String content = new String(Files.readAllBytes(Paths.get("D:\\Gem\\project-admin\\src\\main\\resources\\pay\\wxv3\\apiclient_key.pem")), "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("无效的密钥格式");
|
}
|
}
|
|
|
/**
|
* 功能描述: 验证签名
|
* 注意:使用微信支付平台公钥验签
|
* Wechatpay-Signature 微信返签名
|
* Wechatpay-Serial 微信平台证书序列号
|
*
|
* @return java.lang.String
|
* @author 影子
|
*/
|
@SneakyThrows
|
public static boolean verifySign(HttpServletRequest request, String body) {
|
boolean verify = false;
|
try {
|
String wechatPaySignature = request.getHeader("Wechatpay-Signature");
|
String wechatPayTimestamp = request.getHeader("Wechatpay-Timestamp");
|
String wechatPayNonce = request.getHeader("Wechatpay-Nonce");
|
String wechatPaySerial = request.getHeader("Wechatpay-Serial");
|
//组装签名串
|
String signStr = Stream.of(wechatPayTimestamp, wechatPayNonce, body)
|
.collect(Collectors.joining("\n", "", "\n"));
|
//获取平台证书
|
AutoUpdateCertificatesVerifier verifier = getVerifier(wechatPaySerial);
|
//获取失败 验证失败
|
if (verifier != null) {
|
Signature signature = Signature.getInstance("SHA256withRSA");
|
signature.initVerify(verifier.getValidCertificate());
|
//放入签名串
|
signature.update(signStr.getBytes(StandardCharsets.UTF_8));
|
verify = signature.verify(Base64.getDecoder().decode(wechatPaySignature.getBytes()));
|
}
|
} catch (InvalidKeyException e) {
|
e.printStackTrace();
|
} catch (NoSuchAlgorithmException e) {
|
e.printStackTrace();
|
}
|
return verify;
|
}
|
}
|