1. 前言

牢记一句话:公钥加密,私钥解密;私钥加签,公钥验签。

微信支付V3版本前两篇分别讲了如何对请求做签名和如何获取并刷新微信平台公钥,本篇将继续展开如何对微信支付响应结果的验签。

2. 为什么要对响应验签

微信支付会在回调的HTTP头部中包括回调报文的签名。商户必须验证响应的签名,保证响应确实来自微信支付服务器,避免中间人攻击。而验证响应签名除了需要微信平台的公钥外还需要从请求头的其它参数。

假设以下就是微信支付服务器的响应:

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 02 Apr 2019 12:59:40 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 2204
Connection: keep-alive
Keep-Alive: timeout=8
Content-Language: zh-CN
Request-ID: e2762b10-b6b9-5108-a42c-16fe2422fc8a
Wechatpay-Nonce: c5ac7061fccab6bf3e254dcf98995b8c
Wechatpay-Signature: CtcbzwtQjN8rnOXItEBJ5aQFSnIXESeV28Pr2YEmf9wsDQ8Nx25ytW6FXBCAFdrr0mgqngX3AD9gNzjnNHzSGTPBSsaEkIfhPF4b8YRRTpny88tNLyprXA0GU5ID3DkZHpjFkX1hAp/D0fva2GKjGRLtvYbtUk/OLYqFuzbjt3yOBzJSKQqJsvbXILffgAmX4pKql+Ln+6UPvSCeKwznvtPaEx+9nMBmKu7Wpbqm/+2ksc0XwjD+xlvlECkCxfD/OJ4gN3IurE0fpjxIkvHDiinQmk51BI7zQD8k1znU7r/spPqB+vZjc5ep6DC5wZUpFu5vJ8MoNKjCu8wnzyCFdA==
Wechatpay-Timestamp: 1554209980
Wechatpay-Serial: 5157F09EFDC096DE15EBE81A47057A7232F1B8E1
Cache-Control: no-cache, must-revalidate {"prepay_id":"wx2922034726858082fbd40b511c67630000"}

检查平台证书序列号

微信支付响应的时候会携带一个微信平台证书序列号,从响应头中的Wechatpay-Serial字段中获取值,用来提示我们要使用该序列号的证书来进行验签,如果不存在就需要我们刷新证书,而上一文我们将平台证书序列号和证书以键值对存在HashMap中,我们只需要检查是否存在即可,不存在就刷新。

构造验签名串

从响应结果中获取对应下面方法的三个参数就可以构造出验签名串。

/**
* 构造验签名串.
*
* @param wechatpayTimestamp HTTP头 Wechatpay-Timestamp 中的应答时间戳。
* @param wechatpayNonce HTTP头 Wechatpay-Nonce 中的应答随机串
* @param body 响应体
* @return the string
*/
public String responseSign(String wechatpayTimestamp, String wechatpayNonce, String body) {
return Stream.of(wechatpayTimestamp, wechatpayNonce, body)
.collect(Collectors.joining("\n", "", "\n"));
}

验证签名

待验证的签名从响应头中的Wechatpay-Signature字段中获取,我们使用微信支付平台公钥对验签名串和签名进行SHA256 with RSA签名验证。

   // 构造验签名串
final String signatureStr = responseSign(wechatpayTimestamp, wechatpayNonce, body);
// 加载SHA256withRSA签名器
Signature signer = Signature.getInstance("SHA256withRSA");
// 用微信平台公钥对签名器进行初始化
signer.initVerify(certificate);
// 把我们构造的验签名串更新到签名器中
signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));
// 把请求头中微信服务器返回的签名用Base64解码 并使用签名器进行验证
boolean result = signer.verify(Base64Utils.decodeFromString(wechatpaySignature));

完整的验签代码

/**
* 我方对响应验签,和应答签名做比较,使用微信平台证书.
*
* @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.body 微信服务器的响应体
* @return the boolean
*/
@SneakyThrows
public boolean responseSignVerify(String wechatpaySerial, String wechatpaySignature, String wechatpayTimestamp, String wechatpayNonce, String body) { if (CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) {
refreshCertificate();
}
Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial); final String signatureStr = createSign(wechatpayTimestamp, wechatpayNonce, body);
Signature signer = Signature.getInstance("SHA256withRSA");
signer.initVerify(certificate);
signer.update(signatureStr.getBytes(StandardCharsets.UTF_8)); return signer.verify(Base64Utils.decodeFromString(wechatpaySignature));
}

CERTIFICATE_MAP 平台证书容器可参考上一篇文章。

3. 总结

验签通过就说明我们请求的响应来自微信服务器就可以针对结果进行对应的逻辑处理了,微信支付API无论是V2还是V3都包含了使用Api证书对请求进行加签,对响应结果进行验签的流程,十分考验对密码摘要算法的使用,其它就是组织参数调用Http请求。如果你能够掌握这一能力就会在面试中和工作中占到优势。好了今天分享就到这里,多多关注: 码农小胖哥 获取更多实用的编程干货。

关注公众号:Felordcn 获取更多资讯

个人博客:https://felord.cn

Java中的微信支付(3):API V3对微信服务器响应进行签名验证的更多相关文章

  1. 微信支付开发(12) 认清微信支付v2和v3

    微信支付现在分为v2版和v3版 2014年9月10号之前申请的为v2版,之后申请的为v3版. V2版中的参数有AppIDAppSecret支付专用签名串PaySignKey商户号PartnerID初始 ...

  2. 微信支付之h5方式(非微信内置浏览器中支付)

    这两天完成了公司网站手机和PC端的支付对接,就是支付宝和微信. 对接完后有所感触,我们来聊一聊,微信支付的坑,为什么这么说呢,因为我在对接完支付宝后是很愉快的,基本上在demo上稍加修改就ok了, 对 ...

  3. iOS中 最新微信支付/最全的微信支付教程详解 韩俊强的博客

    每日更新关注:http://weibo.com/hanjunqiang  新浪微博! 亲们, 首先让我们来看一下微信支付的流程吧. 1. 注册微信开放平台,创建应用获取appid,appSecret, ...

  4. 微信支付之01------获取订单微信支付二维码的接口------Java实现

    [ 前言:以前写过一个获取微信二维码支付的接口,发现最近公司新开的项目会经常用到,现在我又翻出代码看了一遍,觉得还是把整个代码流程记下来的好 ] 借鉴博客: 他这篇博客写得不错,挺全的:https:/ ...

  5. iOS中 最新微信支付/最全的微信支付教程具体解释 韩俊强的博客

    亲们, 首先让我们来看一下微信支付的流程吧. 1. 注冊微信开放平台,创建应用获取appid,appSecret,申请支付功能,申请成功之后会返回一些參数. 2. 下载微信支付sdk 3. clien ...

  6. JAVA微信支付——企业付款(企业向微信用户个人付款、转账)

    本地开发环境支付回调调试方法可以参考:https://www.cnblogs.com/pxblog/p/11623053.html 需要自行引入相关依赖 官方文档地址:https://pay.weix ...

  7. 微信支付官方SDK V3 .NET版的坑

    但是支付成功后却不能正确的执行支付结果js回调函数.看看其页面的点击事件是放在asp:Button上面的.我们知道在asp.net webform中,按钮的点击是有页面回调后台的.也就是其实点击了之后 ...

  8. 微信支付JS API使用心得

    微信的接口真的很坑爹,只返回成功或失败,从来不会告诉你为什么失败.这个微信支付的js接口也是调了一个下午才成功,期间踩了不少坑,在这里总结一下,而且把支付接口封装成了一个js文件,这样以后调用就很方便 ...

  9. ThinkPHP 5 整合支付宝微信支付(支付宝H5,微信H5、APP支付、公众号支付)

    因项目没有PC站所以没有写电脑网站支付. Pay.php支付控制器 <?php // +----------------------------------------------------- ...

随机推荐

  1. Python-进程-进程池-原理

    进程 资源集合,调度和分配资源,说到进程就不得不提到线程,线程和进程是密不可分,进程申请了资源,但真正使用资源的是线程,其实本质上类似面向对象的思想,面向对象把数据和数据的操作封装在一个类中,进程把资 ...

  2. Appium的一些问题的总结答案

        问题 1. error: Failed to start an Appium session, err was: Error: Requested a new session but one ...

  3. VUE第一个项目怎么读懂

    VUE介绍 VUE是前端开发框架. 原始的前端开发需要工程师写html.写css.写javascript(js).js是脚本语言,浏览器可以运行js来执行一些js支持的动作,例如点击反馈,下拉菜单.操 ...

  4. Python-joypy和 R-ggridges 峰峦图制作

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 以下文章来源于DataCharm,作者 宁海涛 转载地址 https://www ...

  5. DIV垂直滚动效果源码

    <div id="demo" style="width: 300; overflow: hidden; line-height:24px; height: 100p ...

  6. Morris遍历

    Morris遍历 一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1) 通过利用原树中大量空闲指针的方式,达到节省空间的目的 Morris遍历可以改前中后序的树遍历 思路: 创建一个当 ...

  7. JS常见加密混淆方式

    目录 前端js常见混淆加密保护方式 eval方法等字符串参数 emscripten WebAssembly js混淆实现 JSFuck AAEncode JJEncode 代码压缩 变量名混淆 字符串 ...

  8. .Net Core中使用Grpc

    一.Grpc概述 gRPC 基于如下思想:定义一个服务, 指定其可以被远程调用的方法及其参数和返回类型.gRPC 默认使用protocol buffers作为接口定义语言,来描述服务接口和有效载荷消息 ...

  9. 扫描仪扫描文件处理-imagemagick常用参数

    -resize 宽x高(缩放,不变形) -extent 宽x高(放大,不变形)之前设置:-gravity center(重心居中) -brightness-contrast 亮度x对比度(设置亮度对比 ...

  10. EfCore3的OwnedType会导致Sql效率问题

    最近主导了旗下某核心项目升级到EfCore3 由于之前Core2升级时候也踩过不少的坑很多东西都有规划和准备,整体上还是没出太大问题 但是最近突然发现efcore对于使用了ownedType的生成语句 ...