当发卡应用策略开启 encrypt_enabled 或 sign_enabled 时,verify 与 heartbeat 返回的运行时响应会加密和/或签名。本文说明算法、线格式、密钥来源与客户端处理顺序。
未开启策略时,encrypted = false 且 signature = null,可直接使用明文 payload。
密钥均为应用级,在控制台「卡密 → 接入材料」获取:
平台 hmac_secret 仅用于服务端卡密哈希 / 会话派生,永不下发到终端。
轮换数据密钥或签名密钥后,旧终端须更新材料后再解密 / 验签。
策略 encrypt_enabled = true 时:
三种算法线格式一致:
ciphertext_b64 = Base64( nonce[12] || ciphertext || tag )
- Base64 解码得到字节串
- 前 12 字节为 nonce
- 余下为 AEAD ciphertext(含认证 tag)
- 用对应算法与数据密钥解密,得到 UTF-8 JSON 字符串
- 再
JSON.parse 为明文运行时载荷(字段同 Runtime Payload)
明文加密前为整包 JSON(与未加密时的 payload 对象序列化一致)。
策略 sign_enabled = true 时:
- RSA-2048 + SHA-256
- 填充:
RSASSA-PKCS1-v1_5
- 签名值:Base64
签名原文是字符串,不是整个 data 对象:
验签失败应拒绝会话,勿使用载荷。签名策略已启用但应用密钥缺失时,接口返回业务码 40021。
客户端处理运行时响应时建议:
1. 读取 encrypted / encrypt_algorithm / signature / sign_algorithm
2. 若有 signature:
- 构造签名原文(密文串 或 明文 payload JSON)
- 用应用验签公钥做 RSA-SHA256 校验
- 失败则中止
3. 若 encrypted:
- 按 encrypt_algorithm 解密 ciphertext
- 解析为明文 payload 对象
4. 使用 session_token、heartbeat_interval_secs 等字段
可先验签再解密(加密场景下签名原文是密文字符串,无需先解密)。
加密且签名时的 data 形状:
{
"payload": {
"ciphertext": "Base64(nonce||ciphertext||tag)"
},
"signature": "<Base64 RSA-SHA256 signature>",
"encrypted": true,
"encrypt_algorithm": "AEAD_AES_256_GCM",
"sign_algorithm": "RSA-SHA256"
}
伪代码(逻辑示意):
const { payload, signature, encrypted, encrypt_algorithm } = data;
const signedText = encrypted
? payload.ciphertext
: JSON.stringify(payload);
if (signature) {
assertRsaSha256(publicKeyPem, signedText, signature);
}
const session = encrypted
? JSON.parse(aeadDecrypt(encrypt_algorithm, dataKey, payload.ciphertext))
: payload;
- 卡密明文在服务端存库始终使用
AEAD_AES_256_GCM,与策略运行时 encrypt_algorithm 无关。
- 勿在日志中打印完整卡密明文或数据密钥。
- 算法名以响应字段为准;客户端应按
encrypt_algorithm 分支,勿写死单一算法。