SM2 算法概述与数字签名原理

这一章我们先聊聊 SM2 是个什么东西,它的核心又是怎么跑起来的。我这些年经手的国密项目,十有八九都会碰到 SM2。说实话,刚开始接触时我也觉得它就是椭圆曲线签名那一套,后来踩过坑才明白——细节决定成败。

SM2 是国家密码管理局在 2010 年发布的椭圆曲线公钥密码算法标准(GM/T 0003-2012)。它包含了数字签名、密钥交换和公钥加密三个部分。今天我们重点讲数字签名,因为这个场景最容易出现签名失败率异常的问题。你想想看,一个支付系统如果签名失败率高,用户就收不到通知,那问题可就大了。

为什么是椭圆曲线?

SM2 基于椭圆曲线密码学(ECC),它的核心优势就是——在同样安全强度下,密钥比 RSA 短得多。我记得有个项目要求硬件芯片存储 1000 个公钥,如果用 RSA-2048,光公钥就得 256KB 空间;换成 SM2 的话,每个公钥只要 64 字节,直接省了 4 倍空间。

个人小备忘: 我一般把 SM2 的安全等级对标 256 位椭圆曲线,相当于 RSA-3072。日常场景完全够用,对运算资源也友好。

SM2 使用的曲线是国密局定义的 256 位素域上的椭圆曲线。它的参数是固定的,包括基点和阶。你不用去选曲线,直接用标准参数就行。我曾经见过有人自己乱改曲线参数,结果签名验签死活对不上——得不偿失。

数字签名的核心要素

先理清楚几个必须搞懂的概念:

  • 私钥:一个随机生成的 256 位整数,只有签名者持有。
  • 公钥:私钥乘以基点得到的椭圆曲线点,公开给所有人。
  • 消息:要签名的原文,通常是交易的摘要或文档哈希。
  • 签名结果:由两个整数 (r, s) 组成,r 是点横坐标,s 是私钥和随机数的混合运算结果。

为什么签名失败率会异常?说白了就是 r 或 s 中某个值不满足合法性条件,或者运算过程引入了不确定因素。后面几章我们会专门分析原因,这一章先把原理讲清楚。

SM2 签名生成流程(标准版)

标准流程其实不复杂,我画张图帮你捋一下:

SM2 数字签名流程框架 ① 消息哈希(SM3) ② 生成随机数 k ③ 计算点 (x1, y1) = k*G ④ r = e + x1 (mod n) ⑤ s = (1+dA)⁻¹(k - r·dA) ⑥ 检查 r 和 s 是否合法 关键:步骤③和④必须使用同一次生成的随机数 k,否则签名会泄露私钥

流程看起来简单吧?但仔细看步骤④和⑤:

  • r = e + x1 (mod n) —— 其中 e 是 SM3 哈希结果转换成整数,x1 是椭圆曲线点的横坐标。这里要求 r 不能为 0,不能大于 n(基点阶)。
  • s = (1+dA)⁻¹(k - r·dA) mod n —— dA 是私钥。这里 s 也不能为 0,且需要保证签名是可恢复的。
我曾经踩过的坑: 在某个高性能签名服务中,我还原随机数生成器时错误地复用了同一个随机数 k,结果从两个签名中直接解出了私钥。那个 bug 虽然线上只影响了几十笔交易,但险些酿成安全事故。所以一定注意——随机数 k 必须每次独立生成,且绝不能重复。

签名验证流程

验证方拿到签名 (r, s) 和消息原文后,会做以下操作:

  1. 计算消息哈希 e = SM3(消息)。
  2. 检查 r, s 是否在 [1, n-1] 内,不满足直接拒绝。
  3. 计算 t = r + s (mod n),如果 t = 0 则签名无效。
  4. 计算椭圆曲线点 (x1', y1') = s*G + t*PA(PA 是公钥)。
  5. 计算 R = e + x1' (mod n),如果 R = r 则签名通过。

嗯,你可能会问:为什么验证公式和生成公式不一样?其实它们是等价的,验证只是把生成过程倒过来推了一遍。

关键参数一览

参数 含义 模长 异常风险
n 基点的阶 256 bit 如果随机数 k >= n,签名会失败
dA 私钥 [1, n-2] 生成时 dA = 0 或越界,直接失败
PA 公钥点 两个 256 bit 坐标 无效地址导致验证不过
e 哈希整数 ≤ 256 bit 哈希截断错误会导致签名不一致
(r, s) 签名值 各 256 bit r=0 或 s=0 或 s>n/2 等均不合规

重点记忆: 签名失败率异常往往出现在 r 或 s 恰好等于 0 的概率极低(2⁻²⁵⁶),但在算法实现中,如果随机数生成器质量差、或者模逆运算出错,小概率事件会变成大概率事故。所以后续章节我们会重点排查随机数生成和模逆计算的实现细节。

我的团队曾经踩过的一个典型问题

几年前一个金融项目,SM2 签名验签成功率一直在 99.97% 左右,也就是每 3000 笔就有一笔失败。排查了很久,后来发现是 SM3 哈希结果转整数时,字节序搞反了——大端和小端用混了。你想想看,1000 笔签名里就有三四笔因为字节序不对导致 e 值不同,r 算出来自然不对。修复后成功率直接到了 99.9999% 以上。

这类问题,光看 API 日志很难定位。我们需要一套系统的检测方法,从随机源、哈希转换、模运算到最终签名输出,逐级插入观测点。这就是这门课要做的事情。

总结一下这一章的核心

SM2 数字签名本质上是一个三步走的椭圆曲线运算:哈希、随机数生成、曲线点乘加。每一环都有可能导致签名失败率异常。我个人的习惯是,先把标准流程和参数边界背熟,然后再去对照实际代码找偏差。后面几章,我们会深入每一个环节的失败模式,给出具体的检测和修复方案。