2、SM2 椭圆曲线基础
椭圆曲线密码学 – 先说点直觉
椭圆曲线密码学(ECC),说白了就是在一张特殊的曲线上做加法。不是坐标加法,是点与点之间的合成运算。你能想象两点连一条线,交到曲线上再翻个跟头,得到第三个点吗?就是这样一套几何操作,构成了整个密码体系。
我第一次看这玩意儿也懵。工作几年后回头看,其实核心就一句话:在椭圆曲线上,给你一个点 P,给你一个整数 k,算出 k 个 P 相加的结果 Q 很容易;反过来,给你 P 和 Q,让你反推出 k,几乎不可能。 这就叫椭圆曲线离散对数难题。
我有次给团队新人讲这个,画了满白板的点线交叉,最后用了一个比喻:你站在北京天安门广场,先向东走 3 步,再向北走 5 步,位置明确。但别人只知道你的起点和终点,想猜出你走了几步?嗯,没有地图的话,你只能把整条路枚举一遍——这就是 ECC 之所以安全的原因。
SM2 用的是什么曲线?
SM2 用的是基于素数域 \(F_p\) 的 Weierstrass 曲线,标准方程长这样:
\(y^2 = x^3 + ax + b\)
这里的 \(a\)、\(b\)、\(p\) 都是国家密码管理局定死的常数,不能随便改。下面这张表是 SM2 的官方参数,我建议你直接存成代码常量,别手敲,容易敲错。我就吃过这个亏——当年手动抄参数时少抄了一位十六进制 p,导致运算出来的点根本不在曲线上,排错排了一下午。
| 参数 | 值(十六进制) | 说明 |
|---|---|---|
| p | FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF | 素数模数,256 位 |
| a | FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC | 曲线方程系数 a |
| b | 28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 152AB8F0 2DD8B311... | 曲线方程系数 b |
| G | (Gx, Gy)见标准文档 | 基点,同时也是子群的生成元 |
| n | FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123 | 基点 G 的阶(素数) |
| h | 1 | 余因子,SM2 曲线 h=1 表示整个椭圆曲线阶等于 n |
密钥对生成原理
生成一对 SM2 密钥其实就三步:
- 产生随机数 k——k 是 1 到 n-1 之间的整数。安全要求高的话,要用硬件随机数发生器,不能用伪随机库。我见过用 Python random 模块生成密钥的线上事故……嗯,后果很严重。
- 计算公钥 P = [k]G——这里的 [k]G 代表点乘:把基点 G 和自己相加 k 次。这个过程叫标量乘法。
- 输出 (k, P)——私钥 k 必须保密存储,公钥 P 可以公开。
这里面最核心、也最耗时的运算就是标量乘法。我通常用 Montgomery 阶梯法实现,兼顾效率与侧信道防护。你写 SM2 代码时务必注意:标量乘法必须做到常数时间,否则攻击者能通过执行时间推断私钥位数。
密钥对生成的完整流程
下面这张 SVG 图把整体逻辑画了出来。你可以对照着看,理解每一步的输入输出:
图里标了三个核心步骤,最后那把锁表示私钥与公钥的单向关系。实际工程中,随机数 k 的生成质量直接决定安全性——如果用同一个 k 签过两次不同消息,攻击者就能把私钥算出来。SM2 标准里已有处理措施,但底层库开发者千万不能自己发明随机数方案。
小试牛刀——自己算笔账
你可以拿小参数试验一下。比如用曲线 y² = x³ + 7(比特币的 secp256k1,但不是 SM2),基点 G 取 (2, 3),私钥 k = 5。手动计算 [5]G = G + G + G + G + G。依次做倍点和点加,最后落到某个整数点 P。理解了这个逻辑,再看 SM2 标量乘法就通了。
我当年入门时最困惑的是“椭圆曲线上加法怎么不像数学加法”。后来我写了不到 50 行 Python 脚本,自己实现点加和倍点,打印每一步的坐标,配合画图才彻底搞明白。建议你也动手实践一下。