首页 > 其他分享 >API V3版本签名详解

API V3版本签名详解

时间:2023-02-17 15:59:54浏览次数:38  
标签:String 证书 V3 param API 签名 详解 请求

1. 前言

最近在折腾微信支付,证书还是比较烦人的,所以有必要分享一些经验,减少在开发微信支付时的踩坑。目前微信支付的 API 已经发展到 V3 版本,采用了流行的 Restful 风格。

分享微信支付的难点——签名,虽然有很多好用的 SDK 但是如果想深入了解微信支付还是需要了解一下的。

2. API 证书

为了保证资金敏感数据的安全性,确保业务中的资金往来交易万无一失。目前微信支付第三方签发的权威的 CA 证书(API 证书)中提供的私钥来进行签名。通过商户平台可以设置并获取 API 证书。
切记在第一次设置的时候会提示下载,后面就不再提供下载了,具体参考说明。

设置后找到 zip 压缩包解压,里面有很多文件,对于 java 开发来说只需要关注 apiclient_cert.p12 这个证书文件就行了,它包含了公私钥,需要把它放在服务端并利用 java 解析.p12 文件获取公钥私钥。

务必保证证书在服务器端的安全,它涉及到资金安全。

解析 API 证书

接下来就是证书的解析了,证书的解析有网上很多方法,这里使用比较“正规”的方法来解析,利用 JDK 安全包的 java.security.KeyStore 来解析。

微信支付 API 证书使用了 PKCS12 算法,通过 KeyStore 来获取公私钥对的载体 KeyPair 以及证书序列号 serialNumber,封装了工具类:

import org.springframework.core.io.ClassPathResource;

import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;

/**
 * KeyPairFactory
 *
 * @author dax
 * @since 13:41
 **/
public class KeyPairFactory {

    private KeyStore store;

    private final Object lock = new Object();

    /**
     * 获取公私钥.
     *
     * @param keyPath  the key path
     * @param keyAlias the key alias
     * @param keyPass  password
     * @return the key pair
     */
    public KeyPair createPKCS12(String keyPath, String keyAlias, String keyPass) {
        ClassPathResource resource = new ClassPathResource(keyPath);
        char[] pem = keyPass.toCharArray();
        try {
            synchronized (lock) {
                if (store == null) {
                    synchronized (lock) {
                        store = KeyStore.getInstance("PKCS12");
                        store.load(resource.getInputStream(), pem);
                    }
                }
            }
            X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias);
            certificate.checkValidity();
            // 证书的序列号 也有用
            String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
            // 证书的 公钥
            PublicKey publicKey = certificate.getPublicKey();
            // 证书的私钥
            PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem);

            return new KeyPair(publicKey, storeKey);

        } catch (Exception e) {
            throw new IllegalStateException("Cannot load keys from store: " + resource, e);
        }
    }
}

这个方法中有三个参数,这里必须要说明一下:

  1. keyPath API 证书 apiclient_cert.p12 的 classpath 路径,一般会放在 resources 路径下,当然你可以修改获取证书输入流的方式。
  2. keyAlias 证书的别名,这个微信的文档是没有的,胖哥通过加载证书时进行 DEBUG 获取到该值固定为 Tenpay Certificate 。
  3. keyPass 证书密码,这个默认就是商户号,在其它配置中也需要使用就是 mchid,就是你用超级管理员登录微信商户平台在个人资料中的一串数字。

3. V3 签名

微信支付 V3 版本的签名是在调用具体的微信支付的 API 时在 HTTP 请求头中携带特定的编码串供微信支付服务器进行验证请求来源,确保请求是真实可信的。

签名格式

签名串的具体格式,一共五行一行也不能少,每一行以换行符\n 结束。

HTTP请求方法\n
URL\n
请求时间戳\n
请求随机串\n
请求报文主体\n
  1. HTTP 请求方法 你调用的微信支付 API 所要求的请求方法,比如 APP 支付为 POST。
  2. URL 比如 APP 支付文档中为https://api.mch.weixin.qq.com/v3/pay/transactions/app,除去域名部分得到参与签名的URL。如果请求中有查询参数,URL末尾应附加有'?'和对应的查询字符串。这里为/v3/pay/transactions/app。
  3. 请求时间戳 服务器系统时间戳,保证服务器时间正确并利用 System.currentTimeMillis() / 1000 获取即可。
  4. 请求随机串 找个工具类生成类似 593BEC0C930BF1AFEB40B4A08C8FB242 的字符串就行了。
  5. 请求报文主体 如果是 GET 请求直接为空字符"" ;当请求方法为 POST 或 PUT 时,请使用真实发送的 JSON 报文。图片上传 API,请使用 meta 对应的 JSON 报文。

生成签名

然后使用商户私钥对按照上面格式的待签名串进行 SHA256 with RSA 签名,并对签名结果进行 Base64 编码得到签名值。对应的核心 Java 代码为:

/**
 * V3  SHA256withRSA 签名.
 *
 * @param method       请求方法  GET  POST PUT DELETE 等
 * @param canonicalUrl 例如  https://api.mch.weixin.qq.com/v3/pay/transactions/app?version=1 ——> /v3/pay/transactions/app?version=1
 * @param timestamp    当前时间戳   因为要配置到TOKEN 中所以 签名中的要跟TOKEN 保持一致
 * @param nonceStr     随机字符串  要和TOKEN中的保持一致
 * @param body         请求体 GET 为 "" POST 为JSON
 * @param keyPair      商户API 证书解析的密钥对  实际使用的是其中的私钥
 * @return the string
 */
@SneakyThrows
String sign(String method, String canonicalUrl, long timestamp, String nonceStr, String body, KeyPair keyPair)  {
    String signatureStr = Stream.of(method, canonicalUrl, String.valueOf(timestamp), nonceStr, body)
            .collect(Collectors.joining("\n", "", "\n"));
    Signature sign = Signature.getInstance("SHA256withRSA");
    sign.initSign(keyPair.getPrivate());
    sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
    return Base64Utils.encodeToString(sign.sign());
}

4. 使用签名

签名生成后会同一些参数组成一个 Token 放置到对应 HTTP 请求的 Authorization 请求头中,格式为:
Authorization: WECHATPAY2-SHA256-RSA2048

Token 由以下五部分组成:

  1. 发起请求的商户(包括直连商户、服务商或渠道商)的商户号 mchid
  2. 商户 API 证书序列号 serial_no,用于声明所使用的证书
  3. 请求随机串 nonce_str
  4. 时间戳 timestamp
  5. 签名值 signature

Token 生成的核心代码:

/**
 * 生成Token.
 *
 * @param mchId 商户号
 * @param nonceStr   随机字符串
 * @param timestamp  时间戳
 * @param serialNo   证书序列号
 * @param signature  签名
 * @return the string
 */
String token(String mchId, String nonceStr, long timestamp, String serialNo, String signature) {
    final String TOKEN_PATTERN = "mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"";
    // 生成token
    return String.format(TOKEN_PATTERN,
            wechatPayProperties.getMchId(),
            nonceStr, timestamp, serialNo, signature);
}

将生成的 Token 按照上述格式放入请求头中即可完成签名的使用。

标签:String,证书,V3,param,API,签名,详解,请求
From: https://www.cnblogs.com/wp-leonard/p/17130422.html

相关文章

  • docker容器之dockerfile&docker-compose CMD/entrypoint详解
    docker容器之dockerfile&docker-composeCMD/entrypoint详解本文目录 [隐藏]I.CMDII.ENTRYPOINTIII.示例ENTRYPOINTCMD/command此前的RUN和CMD,我......
  • 将 ASP.Net Core WebApi 应用打包至 Docker 镜像
    将ASP.NetCoreWebApi应用打包至Docker镜像运行环境为Windows10专业版21H1,DockerDesktop3.6.0(67351),DockerEngine20.10.81.ASP.NetCoreRuntime......
  • 图文详解CDC技术,看这一篇就够了!
    这篇文章是对变更数据捕获(CDC)实践的介绍,而不是对特定工具的深入探讨。假设我们正在构建一个简单的Web应用程序。在大多数情况下,此类项目从最小的数据架构开始。例......
  • java基础巩固-详解泛型
    java泛型(generics)为jdk5引入的新特性,泛型提供了编译时类型安全检测机制,可以在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参......
  • 【博学谷学习记录】超强总结,用心分享 | 前端开发 web APIs(四)
    WebAPIs-第4天进一步学习DOM相关知识,实现可交互的网页特效能够插入、删除和替换元素节点能够依据元素节点关系查找节点1日期对象掌握Date日期对象的使用,......
  • CSS 之 z-index 属性详解
    一、简介本文主要是对z-index属性进行详细的讲解,包括其使用场景、属性效果、适用范围等等。本博客的所有代码执行的浏览器环境,都是以Chrome浏览器为准。1、属性作用z-ind......
  • WebAPI例子
    1.WebApiConfig配置API路由usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Web.Http;namespaceWebAPIDemo1{publicstatic......
  • .Net Core WebAPI 序列化时忽略空值字段
    特定实体publicclassAPIResponseModel{///<summary>///返回标识,200:成功,其它:失败///</summary>publicstringCo......
  • hdfs操作——hdfs的shell命令和hdfs的JavaAPI操作
    hdfs解决hadoop海量数据的存储。shell命令(所有hadoopfs可由hdfsdfs代替)(1)在hdfs上创建目录hadoopfs-mkdir目录名(2)本地文件的上传hadoopfs-copyFromLoc......
  • python接口自动化13-API流量回放/锲约测试/自动化测试
    PPL-Tester简介http工具集,通过代理获取到API的请求与响应信息,将这些请求信息进行流量回放/锲约测试或快速生成用例,亦可通过人工进行修改参数化提取、变量引用、断言......