国密SM2加密完全指南:密钥生成、加密解密与数字签名实战
加密编码(更新于 2026年6月2日)
什么是国密 SM2?
SM2 是中国国家密码管理局发布的非对称加密算法,基于椭圆曲线密码学(ECC),取代 RSA 成为国密标准。
| 对比项 | SM2 | RSA 2048 |
|---|---|---|
| 密钥长度 | 256 bit | 2048 bit |
| 安全强度 | 128 bit | 112 bit |
| 签名速度 | ~0.5ms | ~3ms |
| 验证速度 | ~1ms | ~0.1ms |
| 密钥长度比 | 1 | 8 |
| 国标 | GM/T 0003 | PKCS#1 |
SM2 用 256 位密钥达到 RSA 2048 位的安全强度,密钥更短、速度更快。
SM2 的三种用途
| 用途 | 说明 | 对应工具 |
|---|---|---|
| 加密/解密 | 用公钥加密,私钥解密 | SM2 加密 |
| 数字签名 | 用私钥签名,公钥验证 | SM2 签名 |
| 密钥协商 | 双方协商出共享密钥 | ECDH 协议 |
第一步:生成 SM2 密钥对
使用工具库生成
- 打开 SM2 加密工具
- 点击"生成密钥对"
- 获得公钥和私钥
使用命令行生成
# 安装 gmssl
brew install gmssl # macOS
# 或从 https://github.com/guanzhi/GmSSL/releases 下载
# 生成 SM2 密钥对
gmssl sm2 -genkey -out sm2.key
# 查看密钥
gmssl sm2 -in sm2.key -text
使用 JavaScript 生成
import { sm2 } from 'sm-crypto';
// 生成密钥对
const keyPair = sm2.generateKeyPairHex();
// keyPair.privateKey → 私钥(64位十六进制)
// keyPair.publicKey → 公钥(128位十六进制,04 + X + Y)
console.log('私钥:', keyPair.privateKey);
console.log('公钥:', keyPair.publicKey);
密钥格式说明
SM2 公钥格式:
04 || X(32字节) || Y(32字节)
↓
04 + 64位十六进制X + 64位十六进制Y
总长度:130个十六进制字符(65字节)
SM2 私钥格式:
32字节随机数
总长度:64个十六进制字符(32字节)
第二步:SM2 加密与解密
加密流程
明文 → SM2加密(公钥) → 密文(C1 || C3 || C2)
- C1:椭圆曲线点(65字节)
- C3:SM3 哈希值(32字节)
- C2:密文数据
使用工具库加密
- 打开 SM2 加密工具
- 粘贴公钥
- 输入要加密的明文
- 选择密文格式(C1C3C2 或 C1C2C3)
- 点击"加密"获得密文
使用 JavaScript 加密
import { sm2 } from 'sm-crypto';
const publicKey = '04...'; // 130位十六进制
const privateKey = '...'; // 64位十六进制
// 加密(默认 C1C3C2 格式)
const cipherText = sm2.doEncrypt('Hello 国密', publicKey, 1);
// 参数 1 = C1C3C2 格式(国标推荐)
// 参数 0 = C1C2C3 格式(旧版兼容)
console.log('密文:', cipherText);
// 解密
const plainText = sm2.doDecrypt(cipherText, privateKey, 1);
console.log('明文:', plainText); // "Hello 国密"
密文编码格式
| 格式 | 说明 | 使用场景 |
|---|---|---|
| C1C3C2 | 国标推荐格式 | 新项目(默认) |
| C1C2C3 | 旧版格式 | 兼容旧系统 |
| ASN.1 | DER 编码 | Java/C# 互操作 |
第三步:SM2 数字签名
签名流程
消息 → SM3哈希 → SM2签名(私钥) → 签名值(r, s)
使用工具库签名
- 打开 SM2 加密工具
- 切换到"签名"标签
- 输入私钥和待签名消息
- 点击"签名"获得签名值
使用 JavaScript 签名
import { sm2 } from 'sm-crypto';
const privateKey = '...';
const publicKey = '04...';
// 签名(自动对消息做 SM3 哈希)
const signature = sm2.doSignature('待签名消息', privateKey);
console.log('签名:', signature);
// 验证签名
const verified = sm2.doVerifySignature(
'待签名消息',
signature,
publicKey
);
console.log('验证结果:', verified); // true
带用户 ID 的签名
SM2 签名默认使用用户 ID 1234567812345678(国标默认值),可以自定义:
// 签名时指定用户 ID
const sig = sm2.doSignature('消息', privateKey, {
userId: 'customUserId'
});
// 验证时使用相同的用户 ID
const valid = sm2.doVerifySignature('消息', sig, publicKey, {
userId: 'customUserId'
});
SM2 vs RSA:何时选择?
| 场景 | 推荐 | 原因 |
|---|---|---|
| 国内政务系统 | SM2 | 国密合规要求 |
| 国内金融系统 | SM2 | 银联/人行要求 |
| 国际标准系统 | RSA | 国际兼容性 |
| 移动端加密 | SM2 | 密钥短,计算快 |
| 跨境数据交换 | RSA | 国际通用 |
国密三件套:SM2 + SM3 + SM4
| 算法 | 类型 | 用途 | 工具 |
|---|---|---|---|
| SM2 | 非对称 | 加密/签名/密钥协商 | SM2 工具 |
| SM3 | 哈希 | 消息摘要(类似 SHA-256) | SM3 工具 |
| SM4 | 对称 | 数据加密(类似 AES-128) | SM4 工具 |
典型组合:SM2 + SM4 混合加密
1. 生成随机 SM4 密钥
2. 用 SM4 加密大数据(快)
3. 用 SM2 公钥加密 SM4 密钥(安全传输)
4. 发送:SM2(SM4密钥) + SM4(数据)
import { sm2, sm4 } from 'sm-crypto';
// 1. 生成随机 SM4 密钥
const sm4Key = crypto.getRandomValues(new Uint8Array(16));
const sm4KeyHex = Array.from(sm4Key).map(b => b.toString(16).padStart(2, '0')).join('');
// 2. 用 SM4 加密数据
const encryptedData = sm4.encrypt(plainText, sm4KeyHex);
// 3. 用 SM2 加密 SM4 密钥
const encryptedKey = sm2.doEncrypt(sm4KeyHex, sm2PublicKey, 1);
// 发送:encryptedKey + encryptedData
常见问题
SM2 密文长度为什么比明文长很多?
SM2 密文 = C1(65字节)+ C3(32字节)+ C2(明文长度),所以密文比明文多 97 字节。这是非对称加密的特性,大数据建议用 SM2+SM4 混合加密。
SM2 和 ECDSA 有什么区别?
SM2 使用国密指定的椭圆曲线(sm2p256v1),ECDSA 使用 NIST 曲线(P-256)。算法逻辑相似,但曲线参数不同,不兼容。
如何在 Java 中使用 SM2?
// 使用 Bouncy Castle + 国密扩展
Security.addProvider(new BouncyCastleProvider());
// 生成密钥对
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
kpg.initialize(new ECParameterSpec(...)); // SM2 曲线参数
KeyPair kp = kpg.generateKeyPair();
总结
SM2 是国密体系的核心非对称加密算法,256 位密钥即可达到 RSA 2048 位的安全强度。掌握密钥生成、加密解密、数字签名三大操作,配合 SM3 哈希和 SM4 对称加密,即可满足国密合规要求。工具库提供 SM2、SM3、SM4 三个在线工具,浏览器本地处理,数据零上传。
#国密#SM2#非对称加密#数字签名#加密教程