签名方法
为了防止 API 调用过程中被恶意篡改,调用任何一个 API 都需要携带签名,服务端也会对响应进行签名。本文将使用一组测试密钥,以调用扫码收款接口为例,一步一步介绍如何计算签名。
本文中的签名值仅作文档参考,请以实际计算结果为准。
签名算法
使用 HMAC-SHA256 算法,以终端密钥(Secret)对签名串进行 HMAC-SHA256 运算,结果进行 Base64 编码。
获取密钥
终端签名密钥在设备激活时由平台分配,通过激活设备接口响应中的 sign_secret 字段返回。激活流程详见设备接入。
假设你的测试参数如下:
| 参数 | 值 |
|---|---|
| 商户号 | 100001 |
| 密钥序列号 | KEY001 |
| 终端密钥(Secret) | test_secret_key_1234567890abcdefghijklmnop |
如何构造请求签名
第一步:构造签名串
签名串一共有 5 个部分,每一行为一个参数,结尾以 \n(换行符,ASCII 编码值为 0x0A)结束,包括最后一行。如果参数本身以 \n 结束,也需要附加一个 \n。
HTTP请求方法\n
URL\n
请求时间戳\n
请求随机串\n
请求报文主体\n
以扫码收款接口为例,逐行说明:
1. 获取 HTTP 请求方法
POST
2. 获取请求的 URL,去除域名部分
/open/cashier/trans/micropay
- 请求 URL 不包含域名和 Query String
- 若存在 Query String,则拼接为
URL?QueryString
3. 获取发起请求时的系统当前时间戳,即格林威治时间 1970 年 01 月 01 日 00 时 00 分 00 秒起至现在的总秒数。服务端会拒绝处理很久之前发起的请求,请保持自身系统的时间准确。
1715049600
4. 生成一个请求随机串,推荐调用随机数函数生成,将得到的值转换为字符串。
a1b2c3d4e5f67890
5. 获取请求中的请求报文主体(request body),即原始 JSON 字符串。
{"amount":100,"out_trade_no":"TS20260507000001","auth_code":"134567890123456789"}
- 请求体为原始 JSON 字符串,GET 请求时请求体为空字符串
- 计算签名时 body 是怎样的,发起请求时 body 就应该是怎样的
6. 按照上述规则,构造的请求签名串如下:
POST\n
/open/cashier/trans/micropay\n
1715049600\n
a1b2c3d4e5f67890\n
{"amount":100,"out_trade_no":"TS20260507000001","auth_code":"134567890123456789"}\n
当请求报文主体为空串时,只需附加一个 \n,例如:
GET\n
/open/cashier/terminal/info\n
1715049600\n
a1b2c3d4e5f67890\n
\n
第二步:计算签名值
使用终端密钥(Secret)对签名串进行 HMAC-SHA256 运算,结果进行 Base64 编码得到签名值。
使用命令行演示(将签名串保存为 sign_string.txt):
echo -n -e 'POST\n/open/cashier/trans/micropay\n1715049600\na1b2c3d4e5f67890\n{"amount":100,"out_trade_no":"TS20260507000001","auth_code":"134567890123456789"}\n' \
| openssl dgst -sha256 -hmac "test_secret_key_1234567890abcdefghijklmnop" -binary \
| openssl base64 -A
得出的签名值如下:
qwT9ImMZdg67+59lmbPkdMX2Jj70og6hNdIha/yQQDs=
第三步:设置 Authorization Header
将签名信息放入 HTTP 请求头 Authorization 中,格式如下:
Authorization: KP-CASHIER-HMAC-SHA256 mchid="{商户号}", serial="{密钥序列号}", nonce="{随机串}", timestamp="{时间戳}", signature="{签名值}"
各字段说明:
| 字段 | 说明 |
|---|---|
| mchid | 发起请求的商户号 |
| serial | 密钥序列号,用于声明所使用的密钥 |
| nonce | 请求随机串,需与构造签名串时的随机串保持一致 |
| timestamp | 请求时间戳,需与构造签名串时的时间戳保持一致 |
| signature | 签名值,第二步计算得出的结果 |
以上五项签名信息,无顺序要求。
示例:
Authorization: KP-CASHIER-HMAC-SHA256 mchid="100001", serial="KEY001", nonce="a1b2c3d4e5f67890", timestamp="1715049600", signature="qwT9ImMZdg67+59lmbPkdMX2Jj70og6hNdIha/yQQDs="
第四步:设置客户端信息 Header
每个请求需在 Ikudot-Cashier-Client Header 中携带客户端信息,详见客户端信息。
完整请求示例
组合以上步骤,一个包含了签名的完整 HTTP 请求如下:
curl -X POST \
https://xxxx/open/cashier/trans/micropay \
-H 'Authorization: KP-CASHIER-HMAC-SHA256 mchid="100001", serial="KEY001", nonce="a1b2c3d4e5f67890", timestamp="1715049600", signature="qwT9ImMZdg67+59lmbPkdMX2Jj70og6hNdIha/yQQDs="' \
-H 'Ikudot-Cashier-Client: sn="SN-DEVICE-001", name="收银台A01", ip="192.168.1.100", app="cashier-pos", version="2.0.0", session="abc123def456"' \
-H 'Content-Type: application/json' \
-d '{"amount":100,"out_trade_no":"TS20260507000001","auth_code":"134567890123456789"}'
响应验签
服务端对成功响应(HTTP 2xx)会进行签名,签名信息通过以下响应头返回:
| 响应头 | 说明 |
|---|---|
| Ikudot-Cashier-Nonce | 响应随机串 |
| Ikudot-Cashier-Signature | 响应签名值 |
| Ikudot-Cashier-Timestamp | 响应时间戳 |
| Ikudot-Cashier-Serial | 签名密钥序列号 |
| Ikudot-Cashier-Signature-Type | 签名算法类型 |
响应签名串构造
时间戳\n
随机串\n
响应体\n
验签步骤
- 读取响应头中的时间戳、随机串和签名值
- 拼接响应签名串:
时间戳\n随机串\n响应体\n - 使用终端密钥(Secret)验证签名
安全要求
- 时间戳与服务器时间偏差不得超过 30 分钟,否则请求将被拒绝
- 同一随机串(nonce)在时间窗口内不可重复使用,防止重放攻击
- 请求体大小不得超过 1MB