4. SM2椭圆曲线公钥密码算法
SM2算法,说白了就是咱们国家自己的ECC(椭圆曲线密码学)。我在做国密改造项目时,第一个碰到的硬骨头就是它。很多人觉得椭圆曲线很玄乎,其实没那么复杂。你把它想象成一个在椭圆曲线上做点运算的游戏,就对了。
4.1 椭圆曲线基础
椭圆曲线不是椭圆,而是一个方程:y² = x³ + ax + b。SM2用的是特定的参数,我直接给你看核心参数:
| 参数 | 值 | 说明 |
|---|---|---|
| p | FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF | 素数域模数(256位) |
| a | FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC | 曲线系数 |
| b | 28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93 | 曲线系数 |
| G | (x,y) 坐标对 | 基点(生成元) |
| n | FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123 | 基点阶(素数) |
嗯,这里要注意:SM2的曲线参数是固定的,你不能自己随便改。我曾经见过一个项目,有人想「优化」性能,自己换了曲线参数——结果验签死活过不去。后来查了标准才发现,国标里写死了这些值。
核心概念:椭圆曲线上的点构成一个加法群。两个点相加得到第三个点,这个「加法」不是坐标相加,而是通过几何规则(弦切法)计算。私钥就是一个大整数d,公钥就是Q = d * G(G点自加d次)。
4.2 SM2密钥对生成
生成密钥对其实就三步,我习惯这么记:
- 选私钥:在[1, n-2]范围内随机选一个大整数d
- 算公钥:计算Q = d * G(椭圆曲线标量乘法)
- 输出:私钥d(256位),公钥Q(x,y坐标,各256位)
代码实现大概长这样:
// SM2密钥对生成(伪代码)
function generateSM2KeyPair() {
// 1. 生成随机数作为私钥
let d = randomBigInt(1, n - 2);
// 2. 计算公钥 Q = d * G
let Q = pointMultiply(d, G);
// 3. 返回密钥对
return {
privateKey: d, // 256位大整数
publicKey: Q // 椭圆曲线上的点 (x, y)
};
}
我的经验:生成随机数时一定要用密码学安全的随机数生成器(CSPRNG)。我见过有人用Math.random()生成私钥——结果被暴力破解了。别笑,真事。
4.3 SM2签名与验签
SM2签名算法基于椭圆曲线离散对数难题。说白了就是:给你公钥Q和签名(r,s),你没法反推出私钥d。
签名流程:
- 计算消息摘要e = SM3(Z || M),其中Z是用户身份标识
- 生成随机数k ∈ [1, n-1]
- 计算椭圆曲线点(x1, y1) = k * G
- 计算r = (e + x1) mod n,如果r=0或r+k=n则重来
- 计算s = ((1 + d)⁻¹ * (k - r * d)) mod n,如果s=0则重来
- 输出签名(r, s)
验签流程:
- 检查r, s是否在[1, n-1]范围内
- 计算e = SM3(Z || M)
- 计算t = (r + s) mod n,如果t=0则验签失败
- 计算椭圆曲线点(x1, y1) = s * G + t * Q
- 计算R = (e + x1) mod n,如果R == r则验签通过
避坑指南:我曾经在项目中遇到一个坑——签名时每次都要生成新的随机数k。如果两次签名用了相同的k,私钥就会被算出来。索尼PS3当年就是这么翻车的。所以一定要确保k的随机性。
4.4 SM2加密与解密
SM2加密用的是混合加密:椭圆曲线密钥交换 + 对称加密。说白了就是先用椭圆曲线算出一个共享密钥,再用这个密钥加密实际数据。
加密流程:
- 生成随机数k ∈ [1, n-1]
- 计算椭圆曲线点C1 = k * G(这是密文的一部分)
- 计算共享点S = k * Q(Q是接收方公钥)
- 从S的x坐标导出对称密钥t = KDF(x2 || y2, klen)
- 用t加密明文:C2 = M ⊕ t(异或加密)
- 计算校验值C3 = SM3(x2 || M || y2)
- 输出密文C = C1 || C2 || C3
解密流程:
- 从C中提取C1,检查C1是否在曲线上
- 计算共享点S = d * C1(d是接收方私钥)
- 从S的x坐标导出对称密钥t = KDF(x2 || y2, klen)
- 解密:M = C2 ⊕ t
- 校验:计算SM3(x2 || M || y2)是否等于C3
关键点:SM2加密不是直接对数据做椭圆曲线运算,而是用椭圆曲线协商出一个对称密钥,再用对称加密处理数据。这样做的好处是效率高——椭圆曲线运算只做一次,大数据量加密用对称算法。
我做个对比,帮你理解SM2和RSA的区别:
| 特性 | SM2 | RSA |
|---|---|---|
| 密钥长度 | 256位 | 2048位 |
| 签名速度 | 快 | 慢 |
| 加密速度 | 快(混合加密) | 慢(直接加密) |
| 安全性 | 256位 ≈ RSA 3072位 | 2048位 |
| 算法复杂度 | 中等 | 简单 |
你看,SM2用256位的密钥就能达到RSA 3072位的安全强度。这就是椭圆曲线的优势——密钥短、速度快、安全性高。
这张图把SM2的四个核心功能串起来了。密钥生成是基础,签名/验签和加密/解密都依赖它。你注意看,签名和加密用的是不同的运算路径——签名用私钥,验签用公钥;加密用公钥,解密用私钥。这个「非对称」的特性,正是公钥密码的精髓。
我的建议:刚开始接触SM2时,别急着看数学证明。先跑通一个Hello World级别的签名验签程序,感受一下流程。等用熟了,再回头啃椭圆曲线理论,会轻松很多。