2. SM2椭圆曲线公钥密码算法原理归纳总结
SM2算法,说白了就是中国自己的椭圆曲线公钥密码标准。它跟国际上的ECDSA、ECIES很像,但细节上有不少差别。我在做国密合规项目时,最头疼的就是把原来用ECDSA的代码迁移到SM2……嗯,后面会聊到坑点。先看看核心原理。
2.1 椭圆曲线基础
椭圆曲线不是椭圆,而是一条方程 y² = x³ + ax + b 的曲线。群运算定义在曲线上:两个点相加得到第三个点。这构成了一个循环群。
SM2用的是素数域上的曲线。参数由国家密码管理局指定。我看到不少初学者问:“为什么曲线参数是固定的?” 其实这样更安全,因为不用自己去猜哪个曲线好。我习惯直接使用官方推荐的参数组,省心。
| 参数名 | 含义 | SM2推荐值(简写) |
|---|---|---|
p |
素数域 mod | 0xFFFFFFFE…(256位) |
a |
曲线系数 | 0xFFFFFFFE…(减2? 不对,标准值) 等 |
b |
曲线系数 | 0x28E9FA9E… |
G |
基点 | 04 04D7… (32+32字节) |
n |
基点的阶 | 0xFFFFFFFE… |
一个关键点:SM2的基点 G 的阶 n 是一个素数,且约等于 2²⁵⁶。这意味着私钥空间足够大,暴力破解是不可能的。
2.2 密钥对生成
生成过程很简单:
- 随机选一个
d∈ [1, n-2] 作为私钥。 - 计算公钥
P = d * G(标量乘法)。
我个人习惯用操作系统提供的随机数源。有一次我在嵌入式设备上写SM2,发现随机数生成器没做好,生成的私钥重复了——那真是噩梦。从那以后,我每次都会检查随机数质量。
2.3 数字签名(SM2-SM)
SM2签名算法含有一个独特步骤:使用签名者的公钥和用户ID生成一个“签名者标识”的杂凑值。在国际标准ECDSA里没有这个。这叫什么?SM2签名里用了一个Z值,它把公钥和用户ID绑定在一起。
流程大致是:
- 计算
Z = H(ENTLA || IDA || a || b || x_G || y_G || x_P || y_P)。 - 计算消息杂凑
e = H(Z || M)。 - 随机生成
k,计算椭圆曲线点(x₁, y₁) = k*G。 - 计算
r = e + x₁ (mod n),如果r = 0或r + k = n则重试。 - 计算
s = (d + 1)⁻¹ · (k - r·d) mod n。
签名就是 (r, s),各256位。验证时用公钥反向计算。
r + s ≠ 0 和 r ≠ 0。我曾经见过一份实现漏了检查,结果导致签名可被伪造……修复起来很麻烦。
2.4 公钥加密(SM2-ENC)
SM2的公钥加密是基于ECIES的变体。它用了KDF(密钥派生函数)和对称加密。流程如下:
- 随机生成临时私钥
k,计算临时公钥C₁ = k*G。 - 计算共享点
(x₂, y₂) = k * P(P是接收方公钥)。 - 用KDF(x₂ || y₂) 派生出对称密钥
t。 - 用对称加密(SM4)加密明文,得到
C₂。 - 计算杂凑
C₃ = Hash(x₂ || M || y₂)。
密文 = C₁ || C₃ || C₂。
注意:SM2加密输出比明文多了约96字节(临时公钥65字节 + 杂凑32字节)。这个开销在物联网场景下需要考虑带宽。我之前在NB-IoT项目里就因为这个纠结过传输大小的问题。
2.5 密钥交换(SM2-KE)
SM2的密钥交换协议比ECDH复杂,因为它加入了相互认证和密钥确认。双方各自生成临时密钥对,交换临时公钥,计算共享秘密,然后再通过杂凑确认一致性。
协议包含三个步骤:
- 发起方:生成临时私钥
rA,计算RA = rA * G,发送RA。 - 响应方:生成
rB,计算RB = rB * G,计算共享秘密K = KDF(rB * RA || rB * PA)等,发送RB和确认码。 - 发起方:收到
RB后计算同样的K,验证确认码。
说白了这个协议同时完成了密钥协商和双方身份认证。做应用的时候要注意,协商出的 K 可以派生出会话密钥,而且还能选协商的密钥长度。
2.6 知识体系图
下面这张图把SM2的三个主要功能模块和底层依赖关系说清楚了。
2.7 实现中的几个坑
- 标量乘法性能: 256位的点乘在软件上大约需要几百微秒。嵌入式设备(比如Cortex-M4)可以做到<5ms,但要注意使用上开窗法(wNAF)优化。我曾在一个MCU上裸写,没优化,结果一次签名花了50ms……后来换了混合坐标wNAF,直接降到3ms。
- 随机数质量: SM2私钥、临时k、加密时临时私钥都需要真随机。别用rand()。我推荐用硬件TRNG或系统getrandom()。
- 中间人攻击: 密钥交换协议如果没有验证公钥,容易遭到中间人攻击。SM2-KE用杂凑确认码解决了这个问题。但实现时千万注意确认码的计算方式要和标准一致。
- 曲线参数校验: 接收到公钥时,必须验证公钥在曲线上(即坐标满足曲线方程),并且不是无穷远点。有些实现漏了这步,会被注入无效曲线攻击……我吃过这个亏。
2.8 为什么中国要用SM2?
实质原因是:国外ECC算法的实现可能有后门(比如P-256曲线是否含NSA后门的争议),而SM2的曲线参数由中国自主生成,可以规避供应链风险。而且SM2同时提供了签名、加密、密钥交换三个功能,统一了算法体系,减少了维护成本。
我个人认为,在政务、金融、电力这些关键基础设施中,SM2已经是必选项。即使做国际业务,也要考虑叠加国密支持。你想想看,如果将来国际间互认国密标准,现在铺好SM2就省心了。
以上就是SM2算法原理的核心归纳。下次你遇到需要实现SM2时,可以直接拿这张图对照着做。代码方面,推荐用OpenSSL 1.1.1+ 的国密引擎,或者GmSSL库。自己手写的话注意测试向量——国家密码管理局官网有标准测试数据,跑通它们才算做对了。