1. SM2 算法概述:国密标准背景、椭圆曲线密码基础、SM2 与 ECDSA/E25519 的对比
各位同学,咱们今天正式开讲《SM2 算法性能优化与调优技巧》的第一章。说实话,每次讲国密算法,我都能想起十年前第一次在项目中硬啃 SM2 标准文档的日子——那时候资料少,踩坑多,但回过头看,这些经历反而让我对这套算法有了更深的体感。
这一章,我们不急着写代码,也不急着调优。先把 SM2 的来龙去脉、数学根基,以及它跟 ECDSA、Ed25519 这些主流签名算法的差异理清楚。你想想看,如果连算法为什么长这样都不清楚,后面优化起来很容易走弯路。
1.1 国密标准背景:为什么要有 SM2?
SM2 是国家密码管理局在 2010 年发布的椭圆曲线公钥密码算法标准,编号 GM/T 0003-2012。说白了,它是咱们国家自己的 ECC 算法体系,对标国际上的 ECDSA 和 ECDH。
为什么要搞一套自己的?核心原因就两个:
- 自主可控:国际算法标准由 NIST 等机构制定,背后涉及复杂的政治和技术博弈。我记得 2013 年斯诺登事件后,很多国内政企项目直接要求「必须用国密」,那时候我们团队连夜改代码,印象太深了。
- 安全冗余:SM2 使用的椭圆曲线参数是独立生成的,不依赖 NIST 曲线。虽然目前没有证据说 NIST 曲线有后门,但「自己手里有钥匙」总归更踏实。
核心要点:SM2 不是简单的「中国版 ECDSA」。它在签名算法结构、密钥生成方式、哈希使用上都有独立设计。后面我们会详细对比。
目前 SM2 已广泛应用于电子政务、金融 IC 卡、物联网安全芯片等领域。如果你做的是国内合规项目,SM2 基本是必选项。
1.2 椭圆曲线密码基础:SM2 的数学根基
椭圆曲线密码(ECC)的核心,是在一个有限域上的椭圆曲线点群中做离散对数运算。SM2 使用的是 素数域 GF(p) 上的 Weierstrass 方程:
y² = x³ + ax + b (mod p)
其中 4a³ + 27b² ≠ 0 (mod p)
SM2 标准推荐了一条 256 位的曲线,参数如下:
| 参数 | 值(十六进制) | 说明 |
|---|---|---|
| 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 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 的阶(素数) |
个人经验:我在项目中遇到过有人把 p 和 n 搞混,导致密钥生成一直报错。记住:p 是域模数,n 是点群的阶。两者都是 256 位,但数值不同。调试时建议先打印这两个值确认。
SM2 的密钥对生成很简单:
- 私钥 d:随机选取 [1, n-2] 之间的整数
- 公钥 P:P = d * G(椭圆曲线标量乘法)
嗯,这里要注意:SM2 的公钥是 未压缩格式,包含 x 和 y 坐标各 32 字节,共 64 字节。相比某些国际标准支持压缩格式,SM2 在存储上会多 32 字节。这个差异在嵌入式场景下影响不小,后面优化章节我们会专门讲。
下面这张图展示了 SM2 椭圆曲线密码体系的核心流程:
1.3 SM2 与 ECDSA / Ed25519 的对比
很多同学会问:SM2 跟 ECDSA 到底有什么区别?跟现在很火的 Ed25519 比又怎么样?
我直接说结论:SM2 在签名算法结构上跟 ECDSA 有本质差异,在曲线选择上跟 Ed25519 走的是完全不同的路线。
先看签名算法的对比:
| 对比项 | SM2 | ECDSA | Ed25519 |
|---|---|---|---|
| 曲线形式 | Weierstrass y² = x³ + ax + b | Weierstrass(NIST P-256 等) | Edwards x² + y² = 1 + dx²y² |
| 签名方程 | s = (1+d)⁻¹ · (k - r·d) mod n | s = k⁻¹ · (e + r·d) mod n | 基于 Schnorr 变体 |
| 哈希使用 | 先算 ZA(用户标识+曲线参数),再算 e = H(ZA || M) | 直接 e = H(M) | H(R || P || M) |
| 签名长度 | 64 字节(r, s 各 32) | 64 字节(r, s 各 32) | 64 字节 |
| 公钥长度 | 64 字节(未压缩) | 32 或 64 字节 | 32 字节 |
| 抗量子性 | 弱(同 ECC 体系) | 弱 | 弱 |
避坑指南:我曾经在对接某银行系统时,对方把 SM2 签名当 ECDSA 处理,直接用 ECDSA 验签流程去验 SM2 签名,结果当然验不过。SM2 的签名方程里有个 (1+d)⁻¹ 的逆元计算,ECDSA 没有。这个差异导致验签算法完全不同,千万别想当然。
再说性能对比。Ed25519 在签名和验签速度上通常比 SM2 快 2-3 倍,原因在于:
- Edwards 曲线的点加公式更简单,不需要求逆元
- Ed25519 的标量乘实现可以充分利用固定基点预计算
- SM2 的 Weierstrass 曲线在通用实现中需要更多的模逆运算
但 SM2 也有自己的优势:
- 标准成熟:国密标准对密钥生成、签名、加密、密钥交换都有完整定义,生态完善
- 合规性:国内商用密码应用必须使用国密算法,这是硬性要求
- 参数可控:曲线参数由国密局指定,不存在「信任第三方曲线」的问题
我的建议:如果你的项目只在国内跑,且涉及合规审查,直接上 SM2 没得选。如果是海外项目或对性能极度敏感(比如高频交易),Ed25519 是更好的选择。但话说回来,SM2 经过优化后,在主流 CPU 上签名速度也能达到每秒数万次,绝大多数场景够用了。
最后提一句,SM2 的密钥交换协议(类似 ECDH)在实现上比 ECDSA 复杂一些,因为它需要双方交互两次,并且要验证对方公钥的合法性。这部分我们在后面「密钥交换优化」章节会详细展开。