SM2 密钥对生成

密钥对生成,说白了就是选一个随机数当私钥,再算出对应的公钥。SM2 基于椭圆曲线,这东西比 RSA 高效不少。我一直觉得,理解参数比背公式更重要。

椭圆曲线参数

SM2 用的是国标推荐的曲线参数。全是固定值,你不需要自己推导。我刚开始学的时候总想自己造一条曲线,后来被同事拦住了——造曲线容易挖坑,标准参数经过验证,直接用就好。

SM2 曲线定义在素域 Fp 上,方程为:

y² = x³ + ax + b (mod p)

关键参数如下:

参数说明值(十六进制)
p素数模数,256 位FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF
a方程系数FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC
b方程系数28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93
G基点(x, y 坐标)32C4AE2C 1F198119 5F990446 6A39C994 …
n基点的阶(质数)FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123
h余因子01

核心要点:n 的长度和 p 一样都是 256 位,保证了安全性。我用 SM2 做过一次签名模块,如果 n 选错,密钥对可能无法通过标准验证。

密钥生成算法

密钥生成流程并不复杂。私钥是一个随机大整数 dA,范围在 [1, n-2] 之间。公钥是 PA = dA · G(椭圆曲线点乘)。

具体步骤:

  1. 使用密码学安全的随机数生成器产生 dA。
  2. 检查 dA 是否在合法区间内。
  3. 计算公钥点 PA = dA · G。
  4. 验证 PA 不是无穷远点。
  5. 输出 (dA, PA)。

我用 Python 写过一段演示代码,你可以参考:

# SM2 密钥对生成(示意)
from sm2 import SM2Curve, random_bigint

curve = SM2Curve()
dA = random_bigint(1, curve.n - 2)      # 随机私钥
PA = curve.scalar_mult(dA, curve.G)     # 公钥 = dA * G

# 输出
print("私钥:", dA.hex())
print("公钥:", (PA.x.hex(), PA.y.hex()))

小技巧:实际工程中不建议自己实现点乘,一定要用经过审计的密码库,比如 OpenSSL、GmSSL、Bouncy Castle。我曾经自己写过一个点乘,因为 skip 了一次加法公式的边界条件,导致公钥偶尔算错——排查了一整天才发现问题。

随机数质量要求

私钥的安全完全依赖随机数质量。如果随机数可预测,私钥就等于白送。SM2 标准明确要求使用符合 GB/T 32918.5 的随机数发生器。

有哪些坑?我列一下:

  • 熵源不足:嵌入式设备容易缺真随机数,别只用系统时间 + PID 凑数。
  • 相同随机数:两个用户不能生成一样的私钥。概率极低,但万一发生,整个系统完蛋。
  • 伪随机算法弱:比如用常规的 rand(),周期太短,不安全。

⚠ 曾经踩过的坑:某次在 IoT 设备上集成 SM2,随机数我用了硬件 TRNG,但忘了检查输出是否全零。结果有千分之一的概率私钥全是 0x00……公钥直接算出来全是 0。从那以后我不光加检查,还定期做 SP800-22 随机性测试。

随机数质量要求简表:

要求说明
不可预测性攻击者无法通过历史输出推测未来私钥
独立同分布每次生成独立,且符合均匀分布
抗侧信道随机数生成过程不泄漏内部状态
通过统计测试推荐通过 NIST SP800-22 / GM/T 0005 测试

你想想看,如果随机数生成器被旁路攻击,整个密钥体系就形同虚设。所以我在项目里一般会双路混合:硬件 TRNG 做种子,再用 CTR_DRBG 扩展输出。既保证熵,又保证吞吐。

SM2 密钥对生成流程图 椭圆曲线参数 p, a, b, G, n, h 随机数生成器 符合 GB/T 32918.5 私钥 dA 随机整数 ∈ [1, n-2] 点乘运算 PA = dA · G 公钥 PA 椭圆曲线上的点 验证 PA ≠ O 输出 (dA, PA) —— 密钥对就绪 说明

最后提醒一点:私钥一定要安全存储。生成后如果明文落盘,等于把门钥匙放在门口垫子下。我习惯用硬件安全模块(HSM)或 CPU 安全区(TEE)保护私钥。生成阶段就别让它离开安全边界。