签名流程概览
准备数据
构建待签名字符串
计算 HMAC-SM3
Base64 编码
构造请求
详细步骤说明
1. 准备必需数据
| 参数 | 说明 |
|---|---|
clientId |
服务端分配的客户端标识 (例: your_client_id) |
secret |
服务端分配的明文密钥(仅用于签名计算,禁止传输!) |
timestamp |
当前毫秒级Unix时间戳(自1970-01-01 00:00:00 GMT的毫秒数) |
2. 构建待签名字符串
规则:
- 按参数名字母顺序排序
- 格式:
key1=value1&key2=value2
示例:
clientId=your_client_id×tamp=1678886400123
3. HMAC-SM3 计算
算法规范:
- 使用密钥:
secret的 UTF-8 字节 - 数据输入:待签名字符串的 UTF-8 字节
4. Base64 编码
将 HMAC-SM3 结果字节数组转换为 Base64 字符串
5. 请求头设置
X-Client-Id: your_client_id
X-Timestamp: 1678886400123
X-Signature: {生成的Base64签名}
代码示例
时间戳生成
多语言实现
// Java
String timestamp = String.valueOf(System.currentTimeMillis());
# Python
import time
timestamp = str(int(time.time() * 1000))
// JavaScript
const timestamp = String(Date.now());
// PHP
$timestamp = (string)floor(microtime(true) * 1000);
// Go
import ("time"; "strconv")
timestamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
HMAC-SM3 实现
Java (BouncyCastle)
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import java.util.Base64;
public class HmacSm3Example {
public static String generateSignature(String secret, String source) throws Exception {
byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
byte[] dataBytes = source.getBytes(StandardCharsets.UTF_8);
HMac hmac = new HMac(new SM3Digest());
hmac.init(new KeyParameter(keyBytes));
hmac.update(dataBytes, 0, dataBytes.length);
byte[] result = new byte[32]; // SM3输出长度为32字节
hmac.doFinal(result, 0);
return Base64.getEncoder().encodeToString(result);
}
}
Python (gmssl)
from gmssl import sm3, func
import hmac
import base64
def hmac_sm3_sign(secret: bytes, message: bytes) -> str:
# 自定义HMAC-SM3实现
block_size = 64 # HMAC block size for SM3
if len(secret) > block_size:
secret = sm3.sm3_hash(secret).encode()
if len(secret) < block_size:
secret += b'\x00' * (block_size - len(secret))
o_key_pad = bytes([0x5c ^ b for b in secret])
i_key_pad = bytes([0x36 ^ b for b in secret])
inner_hash = sm3.sm3_hash(i_key_pad + message)
return sm3.sm3_hash(o_key_pad + bytes.fromhex(inner_hash))
# 使用示例
signature = hmac_sm3_sign(
secret = b"your_plaintext_secret",
message = "clientId=test×tamp=123456789".encode()
)
print(base64.b64encode(bytes.fromhex(signature)).decode())
重要注意事项
必须遵守以下规则:
- 密钥仅用于签名,禁止在请求体/URL中传输
- 时间戳误差需在服务端窗口期内(通常±5分钟)
- 严格按字母顺序排列参数
- 使用完整的毫秒级时间戳(13位数字)
- 测试时与服务端保持时钟同步
请求示例
POST /api/v1/endpoint HTTP/1.1
Host: example.com
X-Client-Id: your_client_id
X-Timestamp: 1678886400123
X-Signature: 7H3z5q9O8k2F+vlSdYrXyLmNjG6PbQwKcEi4AaW1=
{"data": "example payload"}