椭圆曲线基础:有限域上的椭圆曲线、群结构、阶与基点

好,我们正式开始第二讲。上一章我简单提了 SM2 的数学根基是椭圆曲线,这一章我们就把它彻底说透。你可能会问:为什么非要用椭圆曲线?说白了,它能在相对小的密钥长度下提供很高的安全性。RSA 要用 2048 位,SM2 用 256 位就差不多了。这个效率优势,在移动端和物联网场景里特别明显。

1. 什么是椭圆曲线?

先别被名字吓到。椭圆曲线不是椭圆,它是一条三次方程定义的曲线。标准形式是:

y² = x³ + ax + b

其中 4a³ + 27b² ≠ 0,这个条件保证曲线没有奇点(就是没有尖点或自交)。

我刚开始学的时候,总觉得这东西很抽象。后来在项目中做密钥生成模块,才真正体会到——它其实就是一组点 (x, y) 的集合,再加上一个无穷远点 O。

关键点:椭圆曲线上的点,加上一个特殊的“无穷远点”,构成一个阿贝尔群。这个群结构是 SM2 所有运算的基础。

2. 有限域上的椭圆曲线

实际密码学中,我们不在实数域上玩。为什么?因为实数域上的点是连续的,计算机没法精确表示。我们把它搬到有限域上。

SM2 使用的是素数域 Fp,其中 p 是一个大素数。曲线方程变成:

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

所有坐标值都是 0 到 p-1 之间的整数。这样一来,曲线上的点就是有限个了。

我记得第一次实现这个时,踩过一个坑:取模运算要小心负数。在 C 语言里,-3 % 17 的结果是 -3,不是 14。你得手动调整到正数范围。

小技巧:实现模运算时,统一用 ((x % p) + p) % p 来保证结果非负。这个习惯能省很多调试时间。

3. 群结构:点加法运算

椭圆曲线上的点可以“相加”。这个加法不是坐标直接加,而是有几何意义:

  1. 取曲线上两点 P 和 Q,画一条直线穿过它们
  2. 这条直线与曲线交于第三点 R'
  3. R' 关于 x 轴的对称点就是 P + Q 的结果

用公式表示就是:

λ = (y₂ - y₁) / (x₂ - x₁)   (P ≠ Q)
λ = (3x₁² + a) / (2y₁)      (P = Q,即倍点运算)

x₃ = λ² - x₁ - x₂
y₃ = λ(x₁ - x₃) - y₁

注意,这里的除法是模逆运算,不是普通除法。我当年写代码时,直接用 / 除,结果跑出来全是错的。后来才意识到,有限域里的除法要用扩展欧几里得算法求逆元。

避坑指南:我曾经在点加法里忘记处理 P = O(无穷远点)的情况。如果 P 是无穷远点,P + Q 应该直接返回 Q。这个边界条件不处理,后面签名验证全崩。

4. 阶与基点

椭圆曲线上所有点的个数,叫做曲线的阶,记作 #E(Fp)。这个值可以用 Schoof 算法计算,不过我们一般直接用标准参数。

SM2 推荐了一条标准曲线,参数如下:

参数 值(十六进制)
p 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
Gx 32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7
Gy BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0
n FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123

其中 G 是基点,n 是基点的阶。什么意思呢?就是 n 个 G 相加等于无穷远点 O。这个 n 必须是大素数,才能保证安全性。

为什么需要基点?SM2 的密钥对是这样生成的:私钥 d 是一个随机数(1 到 n-1 之间),公钥 P = d * G。只要 d 保密,从 P 反推 d 就是离散对数难题,目前没有有效解法。

5. 核心流程:标量乘法

上面提到的 d * G,就是标量乘法。实现方式有很多,最常用的是双倍-加法算法:

function scalar_mult(d, G):
    result = O  // 无穷远点
    temp = G
    while d > 0:
        if d & 1:
            result = point_add(result, temp)
        temp = point_double(temp)
        d = d >> 1
    return result

这个算法的时间复杂度是 O(log n),对于 256 位的 d,只需要 256 次迭代。每次迭代做一次倍点和一次可能的点加。

我在项目中优化过这个流程。如果直接按公式算点加,每次都要做模逆,而模逆很慢。后来改用雅可比坐标,把模逆次数从每次点加一次,降到最后才做一次。速度提升了将近 3 倍。

性能建议:生产环境中,标量乘法一定要用雅可比坐标或类似投影坐标。否则 256 位的标量乘法,每次要算几百次模逆,性能完全扛不住。

6. 本章知识体系

下面这张图把本章的核心逻辑串起来了。你可以看到,从椭圆曲线定义出发,经过有限域、群结构、阶与基点,最后落到标量乘法——这是 SM2 密钥生成和签名验签的基石。

椭圆曲线基础 · 知识体系 椭圆曲线 y² = x³ + ax + b 有限域 Fp 阿贝尔群 · 点加法 阶 #E(Fp) · 基点 G 标量乘法 Q = d · G 密钥生成 签名 / 验签 说明: 椭圆曲线定义 → 有限域约束 → 群结构提供加法规则 → 阶与基点确定运算空间 → 标量乘法是 SM2 所有密码操作的核心原语。

7. 小结与个人体会

椭圆曲线基础这块,说白了就是三件事:曲线方程、有限域、点加法群。你把这三点吃透了,SM2 的密钥生成和签名验签流程就顺了。

我个人习惯在项目初期先画一张类似的体系图,把每个模块的输入输出标清楚。这样写代码时不会迷路。另外,测试向量一定要用标准库验证过,我见过太多因为点加法实现有 bug 导致签名验签对不上的案例。