Skip to content

签名机制

更新时间:2026/7/26 18:55:04

为保证数据传输安全,DaxPay 支付 API 采用 RSA 非对称加密签名机制。请求、响应和异步回调均需签名验证,防止篡改。

密钥体系

DaxPay 使用 RSA 密钥对进行签名验签:

密钥保管方用途
商户私钥商户服务器请求签名
商户公钥DaxPay 平台平台验证请求签名
平台私钥DaxPay 平台响应/回调签名
平台公钥商户服务器商户验证响应/回调签名

密钥生成

推荐使用 2048 位 RSA 密钥对。可使用 OpenSSL 或 Java KeyPairGenerator 生成。

签名字段位置

签名相关字段在请求/响应 JSON 体内,不在 HTTP 请求头:

字段位置说明
signJSON 体签名值(Base64 编码)
reqIdJSON 体请求唯一标识,防重放
nonceStrJSON 体随机字符串,防重放
reqTimeJSON 体请求时间(yyyy-MM-dd HH:mm:ss,GMT+8)

签名生成步骤

1. 构造待签名字符串

  1. 获取请求 JSON 中的所有字段(含嵌套对象的扁平化展开)。
  2. 排除 sign 字段本身
  3. 排除值为空的字段(null 或空字符串)。
  4. 将剩余字段按 key 的 ASCII 字典序 升序排列。
  5. 拼接为 key1=value1&key2=value2 格式。
嵌套对象与列表的展开规则
  • 嵌套对象使用点号连接:terminal.terminalNo=value
  • 列表元素使用中括号索引:limitPay[0]=value
  • 展开后整体参与排序和拼接

2. RSA 签名

使用商户私钥对待签名字符串进行签名:

  • 算法:SHA256withRSA
  • 编码:UTF-8
  • 输出:Base64 编码字符串

3. 填入 sign 字段

将 Base64 签名结果填入请求 JSON 的 sign 字段。

响应验签

商户收到响应后,使用平台公钥验证 sign 字段:

  1. 获取响应 JSON 中的所有字段。
  2. 移除 sign 字段。
  3. 按与请求签名相同的方式:参数升序 → 拼接。
  4. 使用平台公钥以 SHA256withRSA 验签。

代码示例

Java — 生成签名

java
import java.security.*;
import java.util.*;
import java.util.Base64;

// 1. 构造请求参数(TreeMap 自动按 key 排序)
Map<String, String> params = new TreeMap<>();
params.put("appId", "APP001");
params.put("bizOrderNo", "ORDER20241201001");
params.put("method", "wechat_qr");
params.put("mchNo", "M200000001");
params.put("nonceStr", "5K8264ILTKCH16CQ2502SI8ZNMTM67VS");
params.put("notifyUrl", "https://your-domain.com/notify");
params.put("amount", "100");
params.put("reqId", "REQ20241201001");
params.put("reqTime", "2024-12-01 12:00:00");
params.put("title", "测试商品");

// 2. 拼接待签名字符串(排除 sign,按 ASCII 排序)
String signStr = params.entrySet().stream()
    .map(e -> e.getKey() + "=" + e.getValue())
    .collect(Collectors.joining("&"));

// 3. RSA 签名
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey); // 商户私钥
signature.update(signStr.getBytes(StandardCharsets.UTF_8));
String sign = Base64.getEncoder().encodeToString(signature.sign());

Java — 验签响应/回调

java
// 1. 移除 sign 字段,按 key 排序拼接
String verifyStr = sortedParams.entrySet().stream()
    .filter(e -> !"sign".equalsIgnoreCase(e.getKey()))
    .filter(e -> e.getValue() != null && !e.getValue().isEmpty())
    .map(e -> e.getKey() + "=" + e.getValue())
    .collect(Collectors.joining("&"));

// 2. 使用平台公钥验签
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey); // 平台公钥
signature.update(verifyStr.getBytes(StandardCharsets.UTF_8));
byte[] signBytes = Base64.getDecoder().decode(receivedSign);
boolean valid = signature.verify(signBytes);

if (!valid) {
    throw new RuntimeException("签名验证失败");
}

签名规则要点

规则说明
排序方式按参数 key 的 ASCII 码升序
拼接格式key1=value1&key2=value2(无空格、无转义)
空值处理值为 null 或空字符串的参数不参与签名
sign 排除sign 字段本身不参与签名计算(大小写不敏感)
编码签名字符串统一使用 UTF-8
签名算法SHA256withRSA
输出格式Base64 编码字符串
签名位置JSON 请求体 sign 字段(非 HTTP Header)

基于 GNU LGPL v3.0 协议开源