作者:xkxk22 | 来源:互联网 | 2024-10-11 10:00
APIv3版微信支付平台证书获取与自动更新关于微信证书获取平台证书证书自动更新首次下载证书关于微信证书微信支付开发有两份证书。商户证书:商户平台证书私钥可以对请求进行签名,微信服务
API v3版微信支付平台证书获取与自动更新
关于微信证书
微信支付开发有两份证书。
商户证书:商户平台证书私钥可以对请求进行签名,微信服务器进行验签可以验证请求者的身份和合法性;
微信平台证书:是由微信服务器使用私钥对响应签名之后,我们自己的服务器使用平台证书对响应进行验签,从而可以验证响应的合法性。
获取平台证书
微信官网有对应的API可以获取平台证书,请求之前也需要进行相应的签名,响应的数据大致如下。
若有新旧证书更替,则可能不止一条证书数据存在,且ciphertext证书字段为密文传输,需要对其进行解密才能正常使用。
解密使用的对称密钥是在微信平台(微信平台传送门)进行设置。【商户平台】->【API安全】->设置API v3密钥(注意不是API密钥)
{
"data": [
{
"serial_no": "5157F09EFDC096DE15EBE81A47057A7232F1B8E1",
"effective_time ": "2018-06-08T10:34:56+08:00",
"expire_time ": "2018-12-08T10:34:56+08:00",
"encrypt_certificate": {
"algorithm": "AEAD_AES_256_GCM",
"nonce": "61f9c719728a",
"associated_data": "certificate",
"ciphertext": "sRvt… "
}
},
{
"serial_no": "50062CE505775F070CAB06E697F1BBD1AD4F4D87",
"effective_time ": "2018-12-07T10:34:56+08:00",
"expire_time ": "2020-12-07T10:34:56+08:00",
"encrypt_certificate": {
"algorithm": "AEAD_AES_256_GCM",
"nonce": "35f9c719727b",
"associated_data": "certificate",
"ciphertext": "aBvt… "
}
}
]
}
解密响应体,解密ciphertext证书字段代码。
/** * 使用微信平台证书对响应验签,和应答签名比较 * @param wechatpaySerial response.headers['Wechatpay-Serial'] 当前使用的微信平台证书序列号 * @param wechatpaySignature response.headers['Wechatpay-Signature'] 微信平台签名 * @param wechatpayTimestamp response.headers['Wechatpay-Timestamp'] 微信服务器时间戳 * @param wechatpayNonce response.headers['Wechatpay-Nonce'] 微信服务器提供的随机串 * @param body response.headers['Wechatpay-body'] 微信服务器的响应体 * @return boolean * @throws SignatureException */
public boolean responseSignVerify(String wechatpaySerial, String wechatpaySignature, String wechatpayTimestamp, String wechatpayNonce, String body) {
log.info("WXPayUtil responseSignVerify start : wechatpaySerial = " + wechatpaySerial +
", wechatpaySignature = " + wechatpaySignature + ", wechatpayTimestamp = " + wechatpayTimestamp +
", wechatpayNOnce= " + wechatpayNonce + ", body = " + body);
if(CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) {
refreshCertificate();
}
Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial);
// log.info("WXPayUtil responseSignVerify CERTIFICATE_MAP证书:certificate" + certificate);
// 构造验签名串
final String strSignature = responseSign(wechatpayTimestamp, wechatpayNonce, body);
log.info("WXPayUtil responseSignVerify 构造验签名串 strSignature : " + strSignature);
try {
// 加载 SHA256withRSA 签名器
Signature signer = Signature.getInstance(SIGN_INSTANCE_SHA256WITHTSA);
// 用微信平台公钥对签名器进行初始化
signer.initVerify(certificate);
// 将验签名串更新到签名器中
signer.update(strSignature.getBytes(StandardCharsets.UTF_8));
boolean ret = signer.verify(Base64Utils.decodeFromString(wechatpaySignature));
log.info("WXPayUtil responseSignVerify 验签结果 ret = " + ret);
return ret;
} catch (NoSuchAlgorithmException | SignatureException e) {
// TODO Auto-generated catch block
throw new IllegalStateException(e);
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
throw new IllegalArgumentException(e);
}
}
证书自动更新
微信平台为了保证安全,定期会更换平台证书,这时我们自己的系统就需要自动更新对应证书来验签。
private static final Map<String, Certificate> CERTIFICATE_MAP = new ConcurrentHashMap<String, Certificate>();
/** * 证书动态刷新,将最新的证书放入Map中 * */
public void refreshCertificate(){
WXPayNativeService nativeService = new WXPayNativeService();
java.util.List<WXPlatCertificate> respCertList = nativeService.getWXCertList();
log.info("NativeService-refreshCertificate 请求返回数据为:" + respCertList);
// 选用证书启用时间最晚的证书
List<WXPlatCertificate> wx = respCertList.stream()
.sorted(Comparator.comparing((WXPlatCertificate w) -> w.getEffective_time()))
.collect(Collectors.toList());
WXPlatCertificate wxPlatCertificate = wx.get(0);
WXPlatCertificate wxPlatCertificate = respCertList.get(0);
log.info("NativeService-refreshCertificate wxPlatCertificate 最近的证书为:" + wxPlatCertificate);
// respCertList.stream().sorted()
// 解密返回数据,得到证书
String publicKey = WXPayUtil.decryptResponseBody(WXPayUtil.getConfigProperties("WX_APIV3_SYMMETRIC_KEY"),
wxPlatCertificate.getEncrypt_certificate().getAssociated_data(),
wxPlatCertificate.getEncrypt_certificate().getNonce(),
wxPlatCertificate.getEncrypt_certificate().getCiphertext());
log.info("NativeService-getConfigProperties 解密返回数据,publicKey1:" + publicKey);
try {
final CertificateFactory cf = CertificateFactory.getInstance("X509");
ByteArrayInputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8));
Certificate certificate = cf.generateCertificate(inputStream);
String responseSerialNo = wxPlatCertificate.getSerial_no();
System.out.println("NativeService-getConfigProperties 证书序列号为serial_no:" + responseSerialNo);
// 清空HashMap()
CERTIFICATE_MAP.clear();
// 放入证书
CERTIFICATE_MAP.put(responseSerialNo, certificate);
} catch (CertificateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
首次下载证书
首次下载证书的时候,这时候我们本地是没有证书的,所以下载证书的请求不能进行验签,否则可能会出现循环调用的情况,微信平台提供了工具可以进行第一次的证书下载。(微信证书下载工具)
将jar包下载之后cmd输入命令即可下载。当然也可以选择不进行验签。
java -jar F:\WXpayDemo\CertificateDownloader-1.1.jar
-k erexxxrxxxxxxxxxxxxxxxxjled
-m 15xxxxxxxxx61
-f F:\WXpayDemo\param\apiclient_key.pem
-s 23B3AxxxxxxxxxxxxxxxxxxxxxxxC90B50
-o F:\WXpayDemo
完整命令如下:
java -jar F:\WXpayDemo\CertificateDownloader.jar -k java -jar F:\WXpayDemo\CertificateDownloader-1.1.jar -k erexxxrxxxxxxxxxxxxxxxxjled -m 15xxxxxxxxx61 -f F:\WXpayDemo\param\apiclient_key.pem -s 23B3AxxxxxxxxxxxxxxxxxxxxxxxC90B50 -o F:\WXpayDemo
证书下载完成之后就可以对响应进行验签。
相关文章链接:
API v3版微信支付开发JAVA
API v3版微信支付请求签名
API v3版微信支付 验签