3、SM2算法概述:椭圆曲线密码体制简介、SM2标准规范、密钥对结构及曲线参数

椭圆曲线密码体制简介

椭圆曲线密码(ECC),说白了就是利用椭圆曲线上的点群构造的一种公钥密码体制。你可能听过一句话:ECC 用更短的密钥,就能达到和 RSA 同等级别的安全强度。这不是吹牛。比如 256 位的 ECC 密钥,安全性与 3072 位的 RSA 密钥相当。我当年第一次接触国密改造时,客户要求把 RSA 2048 换成 SM2,第一反应就是“密钥长度差这么多,真能扛得住吗?”结果做完实测,性能和安全都合格。

椭圆曲线长什么样?别被名字吓到,它不是一个椭圆,而是满足方程 \(y^2 = x^3 + ax + b\) 的点集,再加上一个无穷远点 O。我在项目中看到很多新手盯着曲线图半天,其实你只需要记住三件事:点加法(P+Q)、倍点(2P)、以及标量乘法(kP)。这些运算构成了 SM2 加解密、签名验签的基础。

我的习惯:在学 ECC 之前,先画一张点加法的几何图——过 P 和 Q 的直线与曲线交于第三点,关于 x 轴对称就是结果。理解了这个,后面所有运算都是重复套用。

SM2 标准规范

SM2 是国家密码管理局发布的椭圆曲线公钥密码算法标准,正式编号为 GB/T 32918。这套标准包含四个部分:

部分 内容
第1部分 数字签名算法
第2部分 密钥交换协议
第3部分 公钥加密算法
第4部分 公钥基础设施(PKI)中的应用规范

你可能觉得这些名字很眼熟,因为 RSA 也有类似的分组。但 SM2 的每个部分都针对椭圆曲线做了调整,比如签名用的是 SM3 哈希(后面章节会讲),密钥交换多了一步验证。我记得有一次做合规审查,对方问“你们 SM2 签名用的是 SHA256 吗?”我直接回答“不对,必须用 SM3”,否则国密检测直接打回。

注意:SM2 的曲线参数是由国家指定、统一的,不能自己随便生成。我曾经看到一个团队想“优化”曲线参数,结果做出来的密钥对和标准设备无法互通。国密算法的精髓就是“固定参数、统一实现”。

密钥对结构

SM2 的密钥对由两部分组成:

  • 私钥 d:一个随机生成的大整数,范围在 [1, n-1] 之间,其中 n 是基点 G 的阶。
  • 公钥 Q:通过标量乘法 \(Q = dG\) 计算得到的椭圆曲线上的一个点,包含 x 和 y 两个坐标分量。

你想想看,私钥就是一个数字,公钥就是一个点。存储时私钥通常用 32 字节的十六进制串,公钥则是 04||x||y,共 65 字节(未压缩)。我在对接一个硬件密码卡时,发现它要求公钥必须用压缩格式(只存 x+符号位),当时为了兼容踩了不少坑。

下面是一段 Python 风格的密钥对生成伪代码,帮你理解数学过程:

# 假设已经加载了 SM2 曲线参数 p, a, b, G, n
import random

def generate_keypair():
    # 随机生成私钥 d (在 [1, n-1] 范围内)
    d = random.randint(1, n-1)
    # 计算公钥 Q = d * G (使用椭圆曲线点乘)
    Q = point_multiply(d, G, a, p)
    return (d, Q)

避坑指南:我曾经在生产环境发现生成的私钥全是偶数,排查半天发现随机数生成器用错了,用了线性同余。一定要用密码学安全的随机数生成器(比如 os.urandom 或硬件真随机数)。

曲线参数

SM2 使用一条标准的 256 位素域椭圆曲线,参数由中国国家密码管理局指定。这些参数是固定的,任何合规的 SM2 实现都必须使用完全相同的值。

参数 值(十六进制) 说明
p FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF 素数域模数
a FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC 曲线系数 a
b 28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93 曲线系数 b
Gx 32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7 基点 G 的 x 坐标
Gy BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0 基点 G 的 y 坐标
n FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123 基点 G 的阶
h 1 余因子(cofactor)

我把这些参数记在一个小本子上,每次做 SM2 实现都要反复核对。尤其是 p 和 n ,前者决定域的大小,后者决定了私钥的有效范围。有一次我把 p 和 n 写反了,结果密钥交换时对方总是算不对,花了半天才找到原因。

另外注意余因子 h = 1,这意味着曲线上的点群阶等于 n,没有需要额外处理的小子群。相比某些 ECC 曲线(比如 Curve25519 的 cofactor=8),SM2 在这方面简单很多——也算个好处吧。

SM2 密钥对生成流程 SM2 曲线参数 基点 G 安全随机数 私钥 d 公钥 Q = d·G SM2 密钥对 私钥 d 由安全随机数生成,公钥 Q 通过点乘 d·G 计算,最终组成密钥对

个人经验:如果你在实现 SM2 密钥对生成,建议先用标准测试向量(比如 GB/T 32918 附录中的例子)验证你的点乘和参数是否正确。我每次集成新的密码库都会跑一遍,避免出现“公钥计算错误但看起来像那么回事”的隐蔽 bug。