國密 SM2 加密完全指南:金鑰生成、加密解密與數位簽章實戰
什麼是國密 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#非对称加密#数字签名#加密教程