GMKitX
纯 TypeScript 国密算法工具集
gmkitx 是一套纯 TypeScript 实现的密码学工具集,覆盖 SM2 / SM3 / SM4 / ZUC 等国密算法,并提供 SHA 系列国际摘要算法。当前包不包含 SM9,也不会在 TypeScript 侧包装 C、WASM 或 native runtime。
目标是提供一套同构(Isomorphic)的代码库,让开发者在 Node.js 与现代浏览器中使用一致的 API 进行加密、解密、签名与哈希运算。
核心特性
我们强调一致性与可维护性并重:
- 同构运行:一套代码无缝运行于 Node.js (>= 18) 与浏览器环境,无需 polyfill。
- 双重范式:既支持 函数式(Functional) 调用,也保留 面向对象(OOP) 封装。
- 互操作友好:支持常见密文格式与编码(Hex/Base64、C1C3C2/C1C2C3、ASN.1 DER)。
- 按需加载:Tree-shaking 友好,可按算法模块引入。
- 类型完整:内建
.d.ts类型定义,编码即文档。
支持矩阵与边界
| 算法 | TypeScript 当前范围 | 主要格式/参数 | 不支持或注意 |
|---|---|---|---|
| SM2 | 密钥生成、加解密、签名/验签、密钥交换 | C1C3C2 默认,可选 C1C2C3;签名支持 raw / der / auto;输入输出支持 hex/base64 |
SM2 加密有随机性,测试不固定完整密文;大数据建议 SM2 + SM4 混合加密 |
| SM3 | 摘要、HMAC、流式更新 | hex/base64 输出 | 无认证加密语义,仅用于摘要/MAC 组合 |
| SM4 | ECB / CBC / CTR / CFB / OFB / GCM / CCM |
PKCS7 / ZERO / NONE;AEAD 返回 ciphertext + tag |
ECB 不建议保护敏感数据;AEAD 必须保存并校验 tag |
| ZUC | ZUC-128 密钥流、标准 EEA3 消息加密、EIA3 MAC;保留旧密钥流入口 | key/iv 均为 16 字节;密钥流长度按字节,zucKeystreamWords 按 32-bit word |
不支持 ZUC-256;ZUC 加密不自带完整性保护 |
| SHA | SHA-1/256/384/512 摘要 | hex/base64 输出 | SHA-1 仅用于兼容旧系统;当前不提供 SHA-224 |
| SM9 | 不支持 | 无 | TypeScript 侧没有 SM9,也不包装 native/WASM;如需 SM9,请使用 Java 侧 gmkit-sm9 JNI/GmSSL 模块 |
测试中的 ZUC、SM3、SM4 固定值是本项目 Java / TypeScript 两端对齐使用的项目向量;没有标注为外部标准向量的值,不应被当作标准测试向量来源。
安装与环境
环境要求:Node.js >= 18 或任意支持 ES6+ 的现代浏览器。
# npm
npm install gmkitx
# pnpm (推荐)
pnpm add gmkitx
# yarn
yarn add gmkitx
快速上手
风格一:函数式编程(推荐)
适合现代前端开发,利于 Tree-shaking,代码更简洁。
1) SM2 非对称加密 + 签名
import {
sm2GenerateKeyPair,
sm2Encrypt,
sm2Decrypt,
sm2Sign,
sm2Verify,
SM2CipherMode,
InputFormat,
OutputFormat,
} from 'gmkitx';
const { publicKey, privateKey } = sm2GenerateKeyPair();
const message = '订单明文';
// 加密 / 解密(显式指定密文模式,便于互操作)
const cipherText = sm2Encrypt(publicKey, message, {
mode: SM2CipherMode.C1C3C2,
outputFormat: OutputFormat.BASE64,
});
const plainText = sm2Decrypt(privateKey, cipherText, {
mode: SM2CipherMode.C1C3C2,
inputFormat: InputFormat.BASE64,
});
// 签名 / 验签
const signature = sm2Sign(privateKey, message);
const ok = sm2Verify(publicKey, message, signature);
2) SM3 摘要 + HMAC
import { sm3Digest, sm3Hmac, OutputFormat } from 'gmkitx';
const hexHash = sm3Digest('订单摘要'); // 默认 Hex
const base64Hash = sm3Digest('订单摘要', { outputFormat: OutputFormat.BASE64 });
const mac = sm3Hmac('sm3-secret', '订单摘要');
3) SM4 对称加密(CBC 示例)
import { sm4Encrypt, sm4Decrypt, CipherMode, PaddingMode, OutputFormat } from 'gmkitx';
const key = '0123456789abcdeffedcba9876543210'; // 128 位密钥(Hex)
const iv = 'fedcba98765432100123456789abcdef'; // 128 位 IV(Hex)
const sm4Payload = sm4Encrypt(key, '敏感数据', {
mode: CipherMode.CBC,
padding: PaddingMode.PKCS7,
iv,
outputFormat: OutputFormat.BASE64,
});
const plaintext = sm4Decrypt(key, sm4Payload, {
mode: CipherMode.CBC,
padding: PaddingMode.PKCS7,
iv,
});
风格二:命名空间导入
结构清晰,适合大型项目统一管理加密模块。
import { sm2, sm3, sm4, sha, CipherMode, PaddingMode, SM2CipherMode } from 'gmkitx';
const { publicKey, privateKey } = sm2.generateKeyPair();
const key = '0123456789abcdeffedcba9876543210';
const iv = 'fedcba98765432100123456789abcdef';
// SM2
const cipher = sm2.encrypt(publicKey, '订单数据', { mode: SM2CipherMode.C1C3C2 });
const plain = sm2.decrypt(privateKey, cipher, { mode: SM2CipherMode.C1C3C2 });
const signature = sm2.sign(privateKey, '订单数据');
const verified = sm2.verify(publicKey, '订单数据', signature);
// SM3
const hash = sm3.digest('订单摘要');
// SM4
const sm4Result = sm4.encrypt(key, '敏感数据', {
mode: CipherMode.CBC,
padding: PaddingMode.PKCS7,
iv,
});
const sm4Plain = sm4.decrypt(key, sm4Result, {
mode: CipherMode.CBC,
padding: PaddingMode.PKCS7,
iv,
});
// SHA 国际标准
const sha512Hash = sha.sha512('Hello World');
风格三:浏览器脚本 (CDN)
通过 UMD 构建包,在 HTML 中直接使用全局变量 GMKit。
<script src="https://unpkg.com/gmkitx@<version>/dist/index.global.js"></script>
<script>
const actual = GMKit.sm3Digest('abc');
const expected = '66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0';
if (actual !== expected) throw new Error(`SM3 vector mismatch: ${actual}`);
</script>
将 <version> 替换为 npm 已发布的精确版本。不要在生产页面使用浮动 latest;预发布版本发布前,CDN URL 也不会存在。
推荐使用带算法前缀的顶层名称,例如
sm2Sign、sm3Digest、sm2GenerateKeyPair。 旧的sign、digest、generateKeyPair等裸名继续导出以保证升级兼容,但已标记@deprecated;新代码请使用前缀函数或sm2/sm3命名空间。
当前兼容与安全边界
sm4Encrypt现在返回{ ciphertext, tag?, format }对象;sm4Decrypt可直接接收该对象。zucKeystream(key, iv, length)的length改为 字节数;若需要按 32-bit word,使用zucKeystreamWords。sm2Encrypt的模式参数改为选项对象:sm2Encrypt(pub, data, { mode });二进制解密使用sm2DecryptBytes。sm2Sign / sm2Verify / signatureToXml支持signatureFormat: 'raw' | 'der' | 'auto';DER 输入请显式标注,解析器只接受 canonical DER。- Base64 密文解密支持自动识别;跨语言互操作时建议显式指定
inputFormat: InputFormat.BASE64(SM2 / SM4 / ZUC)。二进制明文分别使用sm2DecryptBytes、sm4DecryptBytes、zucDecryptBytes,避免 UTF-8 解码破坏数据。 - 安全修复:SM2 现在会拒绝非法
mode/signatureFormat;SM4 现在会拒绝奇数长度的 hex key/iv,并严格校验 GCM 标签长度。
API 深度指南
SM2(椭圆曲线公钥密码)
- 加/解密、签名/验签、密钥对生成;默认
C1C3C2,可切换C1C2C3。 - Node/浏览器同构,面向对象与函数式并行。
- 公钥输入支持非压缩格式
04 || x || y(65 字节 / 130 hex)与压缩格式02/03 || x(33 字节 / 66 hex);加密输出中的 C1 当前为非压缩点。 - 解密支持 raw
C1C3C2/C1C2C3,也支持 canonical ASN.1 DER 密文;BER、非最短编码和尾随数据会拒绝。未显式传mode时会先按C1C3C2尝试,再按C1C2C3尝试。 - 签名默认输出 raw
r || s(64 字节 / 128 hex),可指定 DER;验签可指定raw/der/auto。签名和验签必须使用相同userId,默认1234567812345678。
import { SM2, SM2CipherMode, InputFormat, OutputFormat } from 'gmkitx';
const sm2 = SM2.fromPrivateKey(privateKey);
const signature = sm2.sign('核心指令');
const verified = sm2.verify('核心指令', signature);
const cipher = sm2.encrypt('数据', { mode: SM2CipherMode.C1C3C2 });
const plain = sm2.decrypt(cipher);
// DER + Base64 签名示例
const sigDer = sm2.sign('核心指令', { signatureFormat: 'der', outputFormat: OutputFormat.BASE64 });
const ok = sm2.verify('核心指令', sigDer, { signatureFormat: 'der', inputFormat: InputFormat.BASE64 });
SM3(消息摘要)
- 流式更新,Hex/Base64 输出;与 SHA API 对齐。
import { SM3, OutputFormat } from 'gmkitx';
const sm3 = new SM3();
sm3.update('part-1');
sm3.update('part-2');
const hex = sm3.digest(); // 默认 Hex
const base64 = SM3.digest('part-1part-2', { outputFormat: OutputFormat.BASE64 });
实例 digest() 完成后会重置状态,便于复用同一个对象处理下一条消息。若同一消息需要两种编码,应对摘要字节统一编码,或像上例一样重新摘要;不能把第二次 digest() 当成第一次结果的另一种格式。
SM4(分组密码)
- 支持
ECB|CBC|CTR|CFB|OFB|GCM|CCM,PKCS7/NoPadding 可选。
| mode | iv / nonce | padding | tag |
|---|---|---|---|
ECB |
不使用 | PKCS7 / ZERO / NONE |
无 |
CBC |
16 字节 IV | PKCS7 / ZERO / NONE |
无 |
CTR / CFB / OFB |
16 字节 IV | 不使用分组填充,建议 NONE |
无 |
GCM |
12 字节 IV | NONE |
12-16 字节,默认 16;aad 必须与解密端一致 |
CCM |
7-13 字节 nonce,建议 12 | NONE |
4-16 字节偶数,默认 16;aad 必须与解密端一致 |
sm4Encrypt 返回 { ciphertext, tag?, format };GCM/CCM 解密时需传入同一个 iv、aad 和 tag,或直接把加密结果对象传给 sm4Decrypt。
import { SM4, sm4Encrypt, sm4Decrypt, CipherMode, PaddingMode } from 'gmkitx';
const key = '0123456789abcdeffedcba9876543210';
const sm4 = new SM4(key, { mode: CipherMode.GCM, padding: PaddingMode.NONE, iv: '00112233445566778899aabb' });
const result = sm4.encrypt('敏感信息');
const decrypted = sm4.decrypt(result);
// CCM(AEAD)示例:7-13 字节 nonce,支持 AAD
const ccm = sm4Encrypt(key, '敏感信息', { mode: CipherMode.CCM, iv: '00112233445566778899aabb', aad: 'meta', tagLength: 16 });
const ccmPlain = sm4Decrypt(key, ccm, { mode: CipherMode.CCM, iv: '00112233445566778899aabb', aad: 'meta' });
ZUC(祖冲之序列密码)
- 覆盖 128-EEA3(机密性)与 128-EIA3(完整性);同一 key/IV 生成的密钥流绝不能跨消息复用。
- 当前仅实现 ZUC-128,key 与 iv 都必须是 16 字节;
zucEncrypt/zucDecrypt每次都从 IV 起始生成密钥流。 zucDecrypt将结果按 UTF-8 解码为字符串;二进制数据使用zucDecryptBytes。eea3是为旧调用方保留的 EEA3 密钥流入口;按 3GPP 位长度加密消息应使用eea3Encrypt,完整性标签使用eia3。
import { zucEncrypt, zucDecryptBytes, zucKeystream, zucKeystreamWords } from 'gmkitx';
const cipher = zucEncrypt(key, iv, 'Hello ZUC');
const keystream = zucKeystream(key, iv, 32); // 32 bytes keystream
const wordStream = zucKeystreamWords(key, iv, 8); // 8 words
const binaryPlain = zucDecryptBytes(key, iv, cipher); // Uint8Array
SHA(国际标准摘要)
- SHA1/256/384/512 系列,API 与 SM3 一致,便于混合使用。
import { sha } from 'gmkitx';
const hash = sha.sha256('Hello World');
算法选择与安全边界
- 没有“又快又安全又通用”的单一加密方案,算法选择必须按场景做权衡。
- 业务数据加密优先
SM4-GCM或SM4-CCM;CBC/CTR/CFB/OFB仅提供机密性,不提供完整性认证,需额外 MAC。 SM2适合密钥封装、签名验签,不适合直接加密大数据(建议SM2 + SM4混合加密)。ZUC更偏通信协议场景(如 EEA3/EIA3),通用业务通常优先 SM4。SHA-1仅用于兼容旧系统,不建议用于新系统安全场景。SM9当前不属于 TypeScript 包能力范围;本包不提供 C/WASM/native 包装。
Java 对接提示(重点)
- Java
PKCS5Padding在 SM4(16 字节分组)场景下语义上对应前端/Node 的PKCS7。 - Java/BouncyCastle 若使用
SM4/CCM/NoPadding,前端/Node 对应mode: CipherMode.CCM;需显式对齐 nonce(7-13 字节)和 tag 长度。 - SM2 签名格式要显式约定:Java 常见 DER,gmkitx 默认 raw。
- Base64 密文解密支持自动识别;与 Java 等异构系统对接时建议显式传
inputFormat: InputFormat.BASE64(SM2 / SM4 / ZUC)。 - 完整公开 API 清单与 Java 对照边界见
../ts-docs/dev/API-SURFACE.zh-CN.md。
编码与格式
InputFormat / OutputFormat 统一规范密文与签名的编码格式。
import { InputFormat, OutputFormat, sm2Encrypt, sm2Decrypt, sm4Encrypt, sm4Decrypt, CipherMode, PaddingMode } from 'gmkitx';
const sm2Cipher = sm2Encrypt(pubKey, 'hello', { outputFormat: OutputFormat.BASE64 });
const sm2Plain = sm2Decrypt(privKey, sm2Cipher, { inputFormat: InputFormat.BASE64 });
const sm4Result = sm4Encrypt(key, 'hello', { mode: CipherMode.ECB, padding: PaddingMode.PKCS7, outputFormat: OutputFormat.BASE64 });
const sm4Plain = sm4Decrypt(key, sm4Result, { mode: CipherMode.ECB, padding: PaddingMode.PKCS7 }); // 自动读取 result.format
小程序/受限环境提示
- 若运行环境缺少
TextEncoder/TextDecoder,可使用setTextCodec注入自定义 UTF-8 编解码器。 - 默认 RNG 策略为
warn:缺少 CSPRNG 时会警告并使用不安全兼容降级。安全环境使用configureRNG('strict');受限平台通过setCustomRNG注入平台 CSPRNG。 - 可通过
getEnvReport()检查环境能力。
工具箱 (Utils)
gmkitx 暴露了底层的数据处理函数,方便处理编码转换与 ASN.1 结构。
| 分类 | 函数 | 说明 |
|---|---|---|
| 编码 | hexToBytes, bytesToHex |
Hex 字符串与字节数组互转 |
| 编码 | base64ToBytes, bytesToBase64 |
Base64 与字节数组互转 |
| 编码 | stringToBytes, bytesToString |
UTF-8 字符串处理 |
| 编码 | decodeInput, encodeOutput |
输入/输出格式统一编解码 |
| 运算 | xor, rotl |
异或与循环左移 |
| 格式 | rawToDer, derToRaw |
签名的 RAW/DER 格式转换 |
| 随机 | getRandomBytes, configureRNG, setCustomRNG |
随机源与策略控制 |
| 环境 | setTextCodec, getEnvReport |
文本编解码与环境能力报告 |
工程审计命令
# 构建(含告警策略)
npm run build
# 类型检查
npm run type-check
# 单元测试
npm test
# 可运行示例:构建后用 Node 调用 dist
node -e "import('./dist/index.js').then(({ sm3Digest }) => console.log(sm3Digest('你好,国密')))"
# 发布包体积审计(npm pack dry-run)
npm run audit:pack
# 真实打包、临时安装并验证 ESM/CJS/IIFE 与旧兼容别名
npm run test:package
# 文档静态资源体积审计
npm run audit:docs:assets
许可证与第三方组件
gmkitx 自身按 Apache License 2.0 发布。发布构建内联了
@noble/curves 与 @noble/hashes 的 MIT 代码,完整版权和许可证文本见
THIRD_PARTY_NOTICES.md,并随 npm tarball 一同分发。