Java中的微信支付(2):API V3 微信平台证书的获取与刷新

1. 前言
在Java中的微信支付(1):API V3版本签名详解一文中胖哥讲解了微信支付V3版本API的签名,当我方(你自己的服务器)请求微信支付服务器时需要根据我方的API证书对参数进行加签,微信服务器会根据我方签名验签以确定请求来自我方服务器。那么同样的道理我方的服务器也要对微信支付服务器的响应进行鉴别来确定响应真的来自微信支付服务器,这就是验签。验签使用的是【微信支付平台证书公钥】,不是商户API证书。使用商户API证书是验证不过的。今天就来分享一下如何获得微信平台公钥和动态刷新微信平台公钥。
2. 获取微信平台证书公钥
微信平台证书是微信支付平台自己的证书,我们是管不了的,而且是有效期的。
微信服务器会定期更换,所以也要求我方定期获取公钥。而且我们只能通过调用接口/v3/certificates来获得,此接口也需要进行签名(可参考上一篇文章)。你可以获取证书后静态放到服务器上,手动更新静态证书;也可以动态获取一劳永逸。本文采取一劳永逸的办法。
平台证书接口文档:https://wechatpay-api.gitbook.io/wechatpay-api-v3/jie-kou-wen-dang/ping-tai-zheng-shu
3. 证书和回调报文解密
为了保证安全性,微信支付在回调通知和平台证书下载接口中,对关键信息进行了AES-256-GCM加密。也就是说我们拿到响应的信息是被加密的,需要解密后才能获得真正的微信平台证书公钥。响应体大致是这样的,具体根据你调用平台证书接口,应该大差不差是下面这个结构:
{
"data": [
{
"effective_time": "2020-10-21T14:48:49+08:00",
"encrypt_certificate": {
// 加密算法
"algorithm": "AEAD_AES_256_GCM",
// 附加数据包(可能为空)
"associated_data": "certificate",
// Base64编码后的密文
"ciphertext": "",
// 加密使用的随机串初始化向量)
"nonce": "88b4e15a0db9"
},
"expire_time": "2025-10-20T14:48:49+08:00",
// 证书序列号
"serial_no": "217016F42805DD4D5442059D373F98BFC5252599"
}
]
}
你可以使用各种JSON类库取得下面方法的参数进行解密以获取证书,同时这里需要用到APIv3密钥,通用的解密方式为:
/**
* 解密响应体.
*
* @param apiV3Key API V3 KEY API v3密钥 商户平台设置的32位字符串
* @param associatedData response.body.data[i].encrypt_certificate.associated_data
* @param nonce response.body.data[i].encrypt_certificate.nonce
* @param ciphertext response.body.data[i].encrypt_certificate.ciphertext
* @return the string
* @throws GeneralSecurityException the general security exception
*/
public String decryptResponseBody(String apiV3Key,String associatedData, String nonce, String ciphertext) {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES");
GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE, key, spec);
cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
byte[] bytes;
try {
bytes = cipher.doFinal(Base64Utils.decodeFromString(ciphertext));
} catch (GeneralSecurityException e) {
throw new IllegalArgumentException(e);
}
return new String(bytes, StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new IllegalStateException(e);
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IllegalArgumentException(e);
}
}
回调的请求体也是此方法进行解密。
3. 动态刷新
然后就能拿到微信平台证书公钥。然后你可以定义个Map,以证书的序列号为KEY,以证书为Value来动态刷新,关键伪代码:
// 定义全局容器 保存微信平台证书公钥 注意线程安全
private static final Map<String, Certificate> CERTIFICATE_MAP = new ConcurrentHashMap<>();
// 下面是刷新方法 refreshCertificate 的核心代码
String publicKey = decryptResponseBody(associatedData, nonce, ciphertext);
final CertificateFactory cf = CertificateFactory.getInstance("X509");
ByteArrayInputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8));
Certificate certificate = null;
try {
certificate = cf.generateCertificate(inputStream);
} catch (CertificateException e) {
e.printStackTrace();
}
String responseSerialNo = objectNode.get("serial_no").asText();
// 清理HashMap
CERTIFICATE_MAP.clear();
// 放入证书
CERTIFICATE_MAP.put(responseSerialNo, certificate);
动态刷新的策略就很好写了:
// 当证书容器为空 或者 响应提供的证书序列号不在容器中时 就应该刷新了
if (CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) {
refreshCertificate();
}
// 然后调用
Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial);
4. 总结
虽然验签你不做可以拿到其它接口的响应结果,但是从资金安全的角度来说这是十分必要的。同时因为微信平台证书不收我方控制,采取动态刷新也会更加方便,不必再担心过期的问题。本文我们通过调用接口拿到密文并解密获得证书。下一篇我们将通过获得的证书进行签名验证来确保我们的响应是微信服务器发过来的,请关注:码农小胖哥 及时获得相关的更新。
关注公众号:Felordcn 获取更多资讯
Java中的微信支付(2):API V3 微信平台证书的获取与刷新的更多相关文章
- 微信支付官方SDK V3 .NET版的坑
但是支付成功后却不能正确的执行支付结果js回调函数.看看其页面的点击事件是放在asp:Button上面的.我们知道在asp.net webform中,按钮的点击是有页面回调后台的.也就是其实点击了之后 ...
- 微信支付之02------整个微信支付功能----------Java实现
先来看下微信支付官方文档: 1.在官方文档上有很多种支付方式,由于目前我只做过JSAPI和微信扫码支付二种,其他的就不说了. >>>>>第一种微信扫码支付>> ...
- 微信支付——沙箱调试环境getsignkey方法秘钥获取及常见问题说明
官方文档 :https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=23_1 微信支付PC二维码支付:https://www.cnblogs ...
- 【原创分享·微信支付】C# MVC 微信支付教程系列之现金红包
微信支付教程系列之现金红包 最近最弄这个微信支付的功能,然后扫码.公众号支付,这些都做了,闲着无聊,就看了看微信支付的其他功能,发现还有一个叫“现金红包”的玩意,想 ...
- 【原创分享·微信支付】 C# MVC 微信支付教程系列之扫码支付
微信支付教程系列之扫码支付 今天,我们来一起探讨一下这个微信扫码支付.何为扫码支付呢?这里面,扫的码就是二维码了,就是我们经常扫一扫的那种二维码图片,例如,我们自己添 ...
- 【原创分享·微信支付】 C# MVC 微信支付教程系列之公众号支付
微信支付教程系列之公众号支付 今天,我们接着讲微信支付的系列教程,前面,我们讲了这个微信红包和扫码支付.现在,我们讲讲这个公众号支付.公众号支付的应用环境常见的用户通过公众号,然后再通 ...
- 【原创分享·微信支付】C# MVC 微信支付之微信模板消息推送
微信支付之微信模板消息推送 今天我要跟大家分享的是“模板消息”的推送,这玩意呢,你说用途嘛,那还是真真的牛逼呐.原因在哪?就是因为它是依赖微信生存的呀,所以他能不 ...
- 【分享·微信支付】 C# MVC 微信支付教程系列之公众号支付
微信支付教程系列之公众号支付 今天,我们接着讲微信支付的系列教程,前面,我们讲了这个微信红包和扫码支付.现在,我们讲讲这个公众号支付.公众号支付的应用环境常见的用户通过公众号,然后 ...
- #PHP#微信支付 第二篇 JSAPI 调用统一下单接口获取预支付交易数据
上一篇讲到成功获取 openid,本篇要调用微信统一接口创建预支付交易单,并获取到相关数据,以便(后边)在微信内调起H5支付 第三步,调用微信统一下单接口创建预支付交易单 微信统一下单API是微信支付 ...
随机推荐
- DVWA SQL-injection 附python脚本
SQL-Injection low等级 首先我们将dvwa等级调到low 如图 接下来选择SQL Injection,并在提交框中输入正常值1,查看返回结果 接下来检测是否存在注入,分别输入 1' a ...
- 听我的,看完这30道MySQL基础题再去面试
可以微信搜索公众号「 后端技术学堂 」回复「1024」获取50本计算机电子书,回复「进群」拉你进读者技术交流群,文章每周持续更新,我们下期见! 一个典型的互联网产品架构包含接入层.逻辑处理层以及存储层 ...
- Leetcode-栈&队列
20. 有效的括号 https://leetcode-cn.com/problems/valid-parentheses/ 给定一个只包括 '(',')','{','}','[',']' 的字符串,判 ...
- # 095 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 03 封装总结 01 封装知识点总结
095 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...
- 【原创】经验分享:一个小小emoji尽然牵扯出来这么多东西?
前言 之前也分享过很多工作中踩坑的经验: 一个线上问题的思考:Eureka注册中心集群如何实现客户端请求负载及故障转移? [原创]经验分享:一个Content-Length引发的血案(almost.. ...
- Go path/filepath包
path/filepath 标准库path中有的功能filepath全部具备, 所以使用filepath即可. isABS() 判断一个路径是不是绝对路径. package main import ( ...
- Centos7安装MySQL8.0(RPM方式)
人生处处皆学问,工作也是如此!过去不止一次在Linux上安装MySQL,可以说轻车熟路,但是写篇文章总结一下,发现有很多细节值得学习! 安装包选择 为什么用rpm? 在Linux系列上安装软件一般有源 ...
- Vuejs上传
下载 Vuejs上传Vuejs上传 多部分上传Vue组件. 上传器可以选择上传多部分的文件. 这是关于最大的上传尺寸,允许你上传大文件. 如果prop multiple为真,文件列表将在选择文件时呈现 ...
- 【dos】wmic命令
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 磁盘 查看硬盘信息:wmic diskdrive 查看逻辑盘信息:wmic l ...
- 多测师讲解jmeter _图片详解_(全)高级讲师肖sir