椭圆曲线数学基础:有限域、椭圆曲线方程、点加法与标量乘法

说实话,我第一次接触SM2算法时,最头疼的就是这一节。很多人觉得椭圆曲线数学特别高深,但说白了,它只是一套在有限域上做运算的规则,就像我们小学数学里加减乘除一样,只不过换了个“棋盘”而已。今天我就带你把这个棋盘看明白。

1. 有限域:椭圆曲线的“棋盘”

椭圆曲线上的点,不是我们习惯的实数域,而是在一个有限域上定义的。什么叫有限域?你可以把它想象成一个只有有限个元素的集合,元素可以相加、相乘,结果仍然落在这个集合里,而且满足交换律、结合律、分配律。

SM2使用的是一个素域 Fp,其中 p 是一个大素数(256位)。这个域里的元素就是 0, 1, 2, …, p-1,所有运算都要对 p 取模。记得我在项目中第一次测试SM2签名时,因为没注意p的位数,把一个普通整数直接当域元素用了,结果验签一直失败……后来才发现域运算的取模特性,嗯,再也没犯过这种错误。

域类型 定义 常见用途
素域 Fp 模素数 p 的剩余类 SM2、ECDSA
二进制域 F2m GF(2m) 多项式基 某些硬件实现的ECC

关键点:椭圆曲线上的所有运算都在有限域内完成,包括坐标点、系数、加法结果。任何运算结果一旦超出 p,必须先取模再使用。

2. 椭圆曲线方程:SM2的“标准脸”

SM2使用的椭圆曲线方程是:
y² = x³ + ax + b ,其中 a、b 是域元素,并且满足判别式 4a³ + 27b² ≠ 0(保证曲线没有奇异点)。

我个人的习惯是把这条曲线想象成一张“脸”,平面上每个点 (x, y) 都乖乖坐在自己的位置上。但在有限域上,这条曲线不再是连续的,而是由一堆离散的点组成的。所以SM2的安全性依赖于:给定一条椭圆曲线和一个基点 G,要算出 d·G 很容易,但反过来从 d·G 求 d 几乎不可能——这就是传说中的椭圆曲线离散对数问题(ECDLP)。

  • 系数 a、b 公开
  • 素数 p 公开
  • 基点 G 公开
  • 私钥 d 保密
  • 公钥 P = d·G 公开

一个小技巧:SM2选择的参数 p 是 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF(SM2标准定义的素数),你可以把它记作“256位的奇奇怪怪数”。实际做编码时直接调用标准库就好,不用自己实现。

3. 点加法:椭圆曲线的“加法器”

点加法是椭圆曲线密码学的核心操作。两个点 P 和 Q 相加,得到另一个点 R。几何上它像一条直线穿过曲线上的两点,找到第三个交点,再关于 x 轴对称。但在有限域里,我们直接用公式计算。

当 P ≠ Q 时:

设 P = (x₁, y₁), Q = (x₂, y₂)
λ = (y₂ - y₁) / (x₂ - x₁)  (模 p 的除法就是乘以逆元)
x₃ = λ² - x₁ - x₂
y₃ = λ·(x₁ - x₃) - y₁
结果 R = (x₃, y₃)

当 P = Q 时(倍点运算):

λ = (3·x₁² + a) / (2·y₁)
x₃ = λ² - 2·x₁
y₃ = λ·(x₁ - x₃) - y₁

我说实话,刚开始学这些公式的时候特别容易把符号搞反。后来我每次写代码都把 P 和 Q 分别放在两个变量里,算完后再统一取模。你想想看,如果中间某一步忘记取模,整个公钥都会错。

操作 输入 输出 计算量
点加 P ≠ Q P + Q 1次求逆,2次乘法,若干加法
倍点 P = Q 2P 1次求逆,2次乘法,若干加法

注意:当 y₁ = 0 时,倍点公式中分母为0,此时结果定义为无穷远点O。我在一个SM2加解密库中漏判了这个边界,导致对某些特定点做标量乘法时程序崩溃。一定要加异常处理。

4. 标量乘法:椭圆曲线的“乘法器”

标量乘法 k·P 就是把点 P 重复加 k 次。但 k 是256位的大数,如果真的一次一次加,运算量会爆炸。实际中使用双倍-加法算法(Double-and-Add),复杂度只与 k 的二进制位数成正比(O(log k))。

算法核心:

  1. 将 k 表示为二进制形式,从最高位到最低位扫描
  2. 每扫描一位,先做一次倍点
  3. 如果该位为1,再做一次点加
输入:k = (k_{n-1} ... k₀)₂, 点 P
输出:Q = k·P
Q ← O (无穷远点)
for i from n-1 downto 0:
    Q ← 2·Q
    if k_i == 1:
        Q ← Q + P
return Q

我曾经在一份安全测评报告中看到,有些实现为了加速,会使用预计算表(比如窗口法、滑动窗口法),但代价是更大的存储空间。SM2标准推荐使用固定窗口法,我个人建议对性能敏感的场景,窗口大小选4位就够了,再大收益有限。

为什么标量乘法是签名和加密的基础?签名时用私钥 d 对消息哈希做标量乘法;加密时用临时密钥 k 对基点 G 做标量乘法得到共享密钥。每一步都离不开这些基础运算。

5. 知识体系结构图

椭圆曲线数学基础 · 知识体系 椭圆曲线密码学 素域 Fp 曲线方程 y² = x³+ax+b 点加 & 标量乘 模 p 加/乘/逆 判别式 ≠ 0 Double-and-Add算法 无穷远点O处理 基点 G (x,y) 私钥 d · 公钥P = d·G 核心思想 所有运算都在有限域上封闭, ECDLP保证私钥不可逆。

上面这张图概括了本章的核心:有限域是土壤,曲线方程是种子,点加和标量乘是浇灌方式。三者缺一不可。当你理解了这些,再去看SM2的签名和加密流程,就会有种“原来如此”的感觉。

我的一个小习惯:在学习新密码算法时,先手算一个极小的例子(比如在 F23 上做点加),用纸笔检验公式,然后再跑到代码里去验证。这样既能加深理解,也能避免很多低级bug。