TypeScript SM4 使用手册
TypeScript SM4 使用手册
新协议先选择 GCM。它同时加密明文并认证 ciphertext、AAD 和 tag;接收端只有在认证通过后才能得到明文。CCM 也提供认证加密,但 nonce 与消息长度约束不同。
GCM 协议字段
样例使用固定 key 和 nonce 以便测试。生产环境的 key 来自密钥管理系统;每条消息必须在该 key 下使用从未出现过的 nonce。
SM4-GCM 文本和二进制
// 1. 准备参数:SM4 key 为 16 字节 Hex,GCM nonce 为 12 字节 Hex。
const key = '0123456789abcdeffedcba9876543210';
const nonce = '000102030405060708090a0b';
const aad = 'tenant=demo;schema=1';
const plaintext = 'order=GMKIT-DEMO-0001&amount=88.00';
// 2. SM4-GCM 加密:不使用分组填充,密文和 tag 都输出为 Base64。
const encrypted = sm4Encrypt(key, plaintext, {
mode: CipherMode.GCM,
padding: PaddingMode.NONE,
iv: nonce,
aad,
tagLength: 16,
outputFormat: OutputFormat.BASE64,
});
assert.equal(encrypted.format, OutputFormat.BASE64);
assert.equal(typeof encrypted.tag, 'string');
// 3. SM4-GCM 解密:使用同一 nonce、AAD 和 tag 恢复 UTF-8 原文。
const decrypted = sm4Decrypt(key, encrypted, {
mode: CipherMode.GCM,
padding: PaddingMode.NONE,
iv: nonce,
aad,
inputFormat: InputFormat.BASE64,
tagFormat: InputFormat.BASE64,
});
assert.equal(decrypted, plaintext);
// 4. 认证失败断言:修改 tag 后,SM4-GCM 解密必须抛错,不能返回明文。
const tamperedTagBytes = base64ToBytes(encrypted.tag);
tamperedTagBytes[0] ^= 0x01;
const tampered = { ...encrypted, tag: bytesToBase64(tamperedTagBytes) };
assert.throws(() => sm4Decrypt(key, tampered, {
mode: CipherMode.GCM,
padding: PaddingMode.NONE,
iv: nonce,
aad,
inputFormat: InputFormat.BASE64,
tagFormat: InputFormat.BASE64,
}));
// 5. 二进制加密:任意字节必须通过 Uint8Array 输入,不能先转为文本。
const binary = Uint8Array.of(0x00, 0xff, 0x80, 0x41);
const binaryEncrypted = sm4Encrypt(key, binary, {
mode: CipherMode.GCM,
padding: PaddingMode.NONE,
iv: '0c0d0e0f1011121314151617',
aad,
outputFormat: OutputFormat.BASE64,
});
// 6. 二进制解密:使用 sm4DecryptBytes 原样恢复 00 ff 80 41。
const binaryDecrypted = sm4DecryptBytes(key, binaryEncrypted, {
mode: CipherMode.GCM,
padding: PaddingMode.NONE,
iv: '0c0d0e0f1011121314151617',
aad,
inputFormat: InputFormat.BASE64,
tagFormat: InputFormat.BASE64,
});
assert.deepEqual(binaryDecrypted, binary);参数
传入完整 SM4CipherResult 解密时,0.10.1 按对象的 format 解码 ciphertext 和 tag。示例仍写出 inputFormat/tagFormat,使把字段拆开传输后的协议要求也清楚。
认证失败怎么处理
tag、AAD、nonce、ciphertext 或 key 任一不匹配,GCM 解密都会抛出 Error。调用方必须:
- 丢弃本次结果,不使用任何部分明文。
- 对外返回同一类“认证失败”,避免泄露具体失败位置。
- 日志记录消息 ID、schema 和算法,不记录 key、完整明文或可重放的凭据。
认证失败不是“解密后内容为空”,也不是布尔 false。
CCM
将主流程的 mode 改为 CipherMode.CCM 后,还必须重新确认:
- nonce 长度为 7–13 字节;
- tag 长度为 4–16 的偶数字节;
q = 15 - nonceLength,单条消息最大长度为2^(8q) - 1字节;- 相同 key 下 nonce 同样不可重复;
- 加密和解密使用完全相同的 AAD。
协议没有明确要求 CCM 时,不要仅为了“换一种模式”修改 GCM。
非 AEAD 模式
ECB/CBC 的默认填充是 PKCS7;NONE 要求长度是 16 字节倍数;ZERO 无法区分原文尾部零与填充零。流式模式和 AEAD 模式忽略分组填充。
实例复用
SM4 类可以保存 key、mode 和初始配置,但不能让同一个 GCM nonce 安全地重复使用。并发任务使用独立实例或顶层函数,并在调用前为每条消息生成、登记和保存新 nonce。
全部模式、选项、结果对象和类工厂见 TypeScript SM4 API。