TypeScript 摘要与 HMAC 使用手册
TypeScript 摘要与 HMAC 使用手册
摘要用于内容指纹,任何人都能计算;HMAC 使用共享 key,接收方只有持有相同 key 才能重新计算认证值。需要确认发送者身份且双方不能共享 key 时,使用 SM2 签名。
选择算法
SHA-1 不进入新协议,旧系统核对方式见迁移附录。
固定向量、HMAC 与增量状态
// 1. 准备参数:固定向量使用 abc,业务消息使用订单金额。
const plaintext = 'order=GMKIT-DEMO-0001&amount=88.00';
const tampered = 'order=GMKIT-DEMO-0001&amount=99.00';
const hmacKey = 'merchant-demo-key';
// 2. 计算 SM3 摘要:固定向量必须等于 64 个 Hex 字符。
const sm3 = sm3Digest('abc');
assert.equal(
sm3,
'66c7f0f462eeedd9d1f2d46bdc10e4e2'
+ '4167c4875cf2f7a2297da02b8f4ba8e0',
);
// 3. 计算 SHA-2 摘要:SHA-256/384/512 分别输出 32/48/64 字节。
const sha256Digest = sha256('abc');
assert.equal(
sha256Digest,
'ba7816bf8f01cfea414140de5dae2223'
+ 'b00361a396177a9cb410ff61f20015ad',
);
assert.equal(sha384('abc').length, 96);
assert.equal(sha512('abc').length, 128);
// 4. 计算 HMAC:SM3-HMAC 和 HMAC-SHA256 使用共享 key 认证业务消息。
const sm3Mac = sm3Hmac(hmacKey, plaintext);
const sha256Mac = hmacSha256(hmacKey, plaintext);
// 5. HMAC 成功断言:重新计算后使用无显式早退的字节比较。
assert.equal(
constantTimeEqual(hexToBytes(sm3Mac), hexToBytes(sm3Hmac(hmacKey, plaintext))),
true,
);
assert.equal(
constantTimeEqual(hexToBytes(sha256Mac), hexToBytes(hmacSha256(hmacKey, plaintext))),
true,
);
// 6. 篡改断言:金额变化后,两个 HMAC 都必须不同。
assert.equal(
constantTimeEqual(hexToBytes(sm3Mac), hexToBytes(sm3Hmac(hmacKey, tampered))),
false,
);
assert.equal(
constantTimeEqual(hexToBytes(sha256Mac), hexToBytes(hmacSha256(hmacKey, tampered))),
false,
);
// 7. 增量摘要:分块输入与一次性输入结果一致,digest() 后实例自动重置。
const incrementalSm3 = new SM3().update('a').update('bc');
assert.equal(incrementalSm3.digest(), sm3);
assert.equal(incrementalSm3.update('abc').digest(), sm3);
const incrementalSha256 = new SHA256().update('a').update('bc');
assert.equal(incrementalSha256.digest(), sha256Digest);
assert.equal(incrementalSha256.update('abc').digest(), sha256Digest);固定向量只用于核对实现和编码。订单 HMAC 没有写死结果,因为业务 key 应来自密钥管理系统,不能写进源码。
参数与编码
例如协议给出的 key 是 001122... 的 Hex 字节,应写:
hexToBytes(protocolKeyHex) → sm3Hmac(keyBytes, message)直接写 sm3Hmac(protocolKeyHex, message) 会认证 Hex 文本的 UTF-8 字节,得到不同结果。
接收端验证
接收端先按约定编码解码 HMAC,再使用 constantTimeEqual 比较字节。不要使用字符串 === 或手写遇到不同字节就返回的循环。
HMAC 校验失败返回业务层“不接受消息”。摘要不带 key,不能用“摘要相等”证明发送者身份。
增量类的状态
update(data)追加 UTF-8 字符串或原始字节,并返回当前实例。digest()返回结果后自动重置,实例可处理下一条消息。reset()丢弃尚未完成的输入并返回当前实例。SM3.digest(options)可在本次输出覆盖实例格式;SHA256/384/512的实例输出格式通过构造器或setOutputFormat设置。- 一个实例同一时刻只能处理一条消息。并发请求不能共享可变摘要实例。
一次性消息优先用顶层函数;只有流式读取、超大消息或分块协议才需要增量类。
完整成员见 TypeScript SM3 API 和 TypeScript SHA API。