数据签名过程
签名以及验证

1,准备

  • 涉及接口:适用本文档(API Explorer)的所有接口
  • 申请签名key:商户需要申请签名key,用于数据签名和结果验签,签名key请妥善保管勿对外泄露
  • 签名与验证:不建议在web前端存储和进行数据签名验签,推荐在服务端进行签名和响应结果的验签

2,通用签名规则

数据定义

header参数:指httpRequest header里的参数,如Request HeaderResponse Header
path参数:指 API 文档,URL中带有占位符的为path参数;在接口文档描述中,该参数在path栏位
例如:https://host/V2022-03/payment_methods/{customerPaymentMethodId}
该URL中 customerPaymentMethodId 带有占位符
https://host/V2022-03/payment_methods/pm_1526760521989763072
此时pm_1526760521989763072为参数
query参数:指直接跟在请求地址后方的key=value数据(GET方式表单提交、url后追加的参数如:url?key1=val1&key2=val2)
body参数:httpRequest body中的json对象

通用签名步骤

1,将header参数:gateway-no request-id request-time 字段按ascii排序,取其非空值累加,得到数据H
2,将path参数按照参数名的ascii排序,取其值累加得到数据P
3,将query参数按照参数名的ascii排序,取其值累加得到数据Q
4,将body中的数据转为字符串(json对象转为字符串),此为数据B
注意:webhook 请求头中的header参数包含:gateway-no request-id request-time version,比较其他接口多一个 version 字段,则数据H的获取方式:
将header参数:gateway-no request-id request-time version 字段按ascii排序,取其非空值累加,得到数据H
javaScript
java
//bodyJson为接口发送在body中的json对象数据,转为字符串,即为上述的数据B
JSON.stringify(bodyJson);
//jackson
new ObjectMapper().writeValueAsString(bodyJson)
此步骤特别注意:上述代码为转换字符串示意,一定要确保发送的bodyJson(http发送时一般会将json对象序列化)与转换后的字符串一致
5,以上四项,如数据项存在内容(字符长度大于0),则使用点号连接,得到待签名数据H.P.Q.B
6,采用商户key,对待签名数据进行HMAC-SHA256签名:HMAC-SHA256(H.P.Q.B , key),得到签名结果16进制字符串signValue(不区分大小写)
7,将signValue设置到HttpRequest header的sign字段中,发送该请求

4,对请求数据签名

# post请求示例
curl -X POST "https://api.asiabill.com/V2022-03/refund"
-H 'Content-Type: application/json'
-H 'request-id:123456'
-H 'request-time:1646648307486'
-H 'gateway-no:1000001'
-d '{"refundReason":"test refund","tradeNo":"2021212123123123"}'
对于以上请求,生成签名过程如下:
1,得到header参数的ascii顺序值的字符串H:10000011234561646648307486
2,path参数没有,略过该步骤
3,query参数没有,略过该步骤
4,body的json内容转字符串为{"refundReason":"test refund","tradeNo":"2021212123123123"}
5,上述各项非空数据,点号连接:10000011234561646648307486.{"refundReason":"test refund","tradeNo":"2021212123123123"}
6,使用商户key,比如12345678,进行hmac-sha256签名,得到签名结果:
8eb28572747479aedf3cbc4b59a70b5be180841a527449149ef52d480e12951b
7,将上述签名结果,放入header的sign-info字段中即可

5,对结果数据签名(接口响应,webHook通知)

发送请求,服务端处理后,会在http响应header中原样返回gateway-no response-id response-time version 等信息,同时使用上述签名规则,对响应header和body进行签名,签名结果放在响应header的sign-info字段。客户端收到结果后,推荐对结果数据进行签名验证
签名验证步骤
  1. 1.
    读取请求响应header,解析得到gateway-no request-id request-time字段的值,并按照上述签名顺序将存在的值累加,得到字符串H
  2. 2.
    读取响应body,得到json字符串B
  3. 3.
    使用点号连接,得到待签名数据H.B
  4. 4.
    使用商户key,进行hmac-sha256签名得到结果checkSignValue
  5. 5.
    比较响应header中的sign的值是否与4中的checkSignValue相等,如不相等则数据异常,或验证流程未匹配上
注意:webhook 请求头中的header参数包含:gateway-no request-id request-time version,比较其他接口多一个 version 字段,则数据H的获取方式:
将header参数:gateway-no request-id request-time version 字段按ascii排序,取其非空值累加,得到数据H

6,签名代码示例

vue.js
java
php
// 生成签名,HmacSha256后转为16进制字符串,javascript类似
// 引入crypto-js
import CryptoJS from 'crypto-js/crypto-js'
// 按照签名数据规则拼接数据
let sign_info = "";
// 使用key进行签名
let hash = CryptoJS.HmacSHA256(sign_info, key);
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import java.nio.charset.StandardCharsets;
/**
* 生成签名,HmacSha256后转为16进制字符串
*/
class HmacSha256Util {
public static void main(String[] args) throws Exception {
String key = "12345678";
//由请求参数按规则拼接而来,这里是示例数据
String signInfo = "1220000145508010711647341103179.{\"refundReason\":\"test refund\",\"tradeNo\":\"2021212123123123\"}";
//密钥
SecretKey secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
// 实例化Mac
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
//初始化mac
mac.init(secretKey);
//执行消息摘要
byte[] digest = mac.doFinal(signInfo.getBytes(StandardCharsets.UTF_8));
//转为十六进制的字符串
String signValue = new HexBinaryAdapter().marshal(digest);
//得到签名值:7981DD89443E82C2CC0596702A86AA0FC03C77EA5818DF5BB6EE9B03BD465656
//校验时不区分大小写
System.out.println(signValue);
}
}
// 由请求参数按规则拼接而来,这里是示例数据
$sign_str = '1220000145508010711647341103179.{\"refundReason\":\"test refund\",\"tradeNo\":\"2021212123123123\"}';
// 密钥
$sign_key = '12345678';
// HmacSha256后转为16进制字符串
hash_hmac('sha256', $sign_str, $sign_key);