SM2标准介绍:GM/T 0003系列

聊到SM2,绕不开的就是它的标准——GM/T 0003。这玩意儿说白了就是国家密码管理局发布的椭圆曲线公钥密码算法标准,一套完整的定义。我最早接触它还是2016年做政务云迁移的时候,那时候RSA用得正欢,突然要求全上国密,第一件事就是把这一套标准啃下来。

标准结构:一卷四部分

GM/T 0003不是一本书,是一套共有四个部分的系列文件。我习惯叫它“一卷四部分”,每个部分管一块事。你想想看,从底层数学到上层接口,都得覆盖。

部分编号名称核心内容
GM/T 0003.1总则椭圆曲线参数、密钥对生成、公钥验证
GM/T 0003.2数字签名算法签名生成与验签流程、消息摘要与随机数
GM/T 0003.3密钥交换协议双方会话密钥协商、临时密钥派生
GM/T 0003.4公钥加密算法加密解密操作、密文格式、填充方式
避坑指南: 我曾经遇到厂商说“我们的SM2实现了标准”,结果一测只有签名没有加密。后来发现他引的是老版本草案,只涵盖了前两部分。所以迁移时务必确认引用的是完整的GM/T 0003.1 – 0003.4。

总则里藏着哪些硬核参数?

GM/T 0003.1 里头规定了唯一的推荐曲线参数——sm2p256v1。它是一条256位素域上的椭圆曲线,参数固定,不能自己随便改。我见过有人想“优化性能”换曲线,结果导致互认失败。嗯,这里要注意:标准里定义的曲线参数包括p、a、b、G、n、h,一个字节都不能错。

/* sm2p256v1 曲线核心参数(16进制) */
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
h = 1

个人习惯: 我每次迁移时都会先把这组参数放进自测单元,直接调用底层接口比对一遍。参数对了,后面的签名加密才靠谱。

数字签名算法:流程比RSA复杂,但安全

GM/T 0003.2 的签名流程融合了SM3哈希和随机数k,不像RSA那样可以直接拿私钥对哈希签名。为什么要这么设计?说白了,椭圆曲线签名必须引入临时密钥来防止私钥泄露。我个人觉得,这套流程虽然多了一个随机数生成步骤,但抗量子攻击的潜力也更强。

签名生成主要三步:

  1. 用SM3对消息摘要得到e
  2. 生成随机数k,计算椭圆曲线点(x1,y1),得到r = (e + x1) mod n
  3. 用私钥计算s = ((1+ dA)⁻¹ · (k - r · dA)) mod n

验签类似,只是用公钥还原出r',对比r。注意r和s都不能为0,也不能为n,否则重来。我早年写测试时,就踩过随机数k生成不稳定导致r=0的坑。

密钥交换和加密:各有各的设计哲学

GM/T 0003.3 的密钥交换协议,双方各出一次临时密钥就能协商出会话密钥,不需要像RSA那样用公钥加密对称密钥。我更喜欢这种设计,因为它天然具备前向安全性——即使长期私钥泄露,历史会话密钥仍安全。

GM/T 0003.4 的公钥加密算法,密文格式比较复杂:C1 || C2 || C3,其中C1是椭圆曲线点(需要压缩格式?不,标准明确用非压缩),C2是SM4对称加密结果,C3是SM3校验值。我第一次实现时,因为密文顺序搞反导致解密失败,查了两天才发现是C3位置放错了。

小技巧: 如果你在迁移中遇到之前用RSA加密的文件,需要转换成SM2加密格式,记得密文长度会从256字节(RSA-OAEP)变成大概96字节左右(SM2混合加密),但加密内容本身的对称密钥嵌套逻辑完全不同,不能简单替换长度。

知识体系总览

下面这张图把整个GM/T 0003标准体系串了一遍,你看一眼就能明白各部分的关系。

SM2标准体系结构(GM/T 0003系列) GM/T 0003 总纲 0003.1 总则 曲线参数 · 密钥生成 0003.2 数字签名 签名生成 · 验签 0003.3 密钥交换 协商协议 · 派生 0003.4 公钥加密 加密 · 解密 · 格式 注意:实际应用中通常需要同时部署签名(0003.2)和加密(0003.4)两个子功能, 密钥交换协议(0003.3)用于传输层会话协商,一般与TLS协议配合使用。

这张图里你可以看到,底层是总纲,上面分了四个独立的功能模块。迁移的时候,不要一股脑全搬,而是先搞清楚你的场景要哪个模块。比如做电子合同,你只需要签名验签;做VPN,你可能就需要密钥交换和加密两个。

迁移时最容易忽略的细节

说几个实际中容易踩的坑:

  • 密钥对格式: SM2的私钥是一个256位大整数,公钥是椭圆曲线点(x,y),总共64字节,不压缩。RSA的私钥是一对素数乘积,存储方式完全不同。迁移时密钥解析库必须重写。
  • 签名值顺序: GM/T 0003.2 规定签名值是(r,s) —— 先r后s,而有些国际标准是(s,r)顺序。我曾经从第三方厂商接收签名值时,因为是二进制流没有元数据,导致解析错误。一定要在接口文档里明确序列化顺序。
  • 哈希算法绑定: SM2签名和加密内部强制使用SM3哈希,不能换成SHA-256。如果你原来的系统里哈希算法可以灵活切换,迁移后必须固定为SM3。

我的经验: 在迁移计划阶段,我会先输出一份“标准覆盖检查清单”,把GM/T 0003.1到.4的每一项要求列出来,逐项比对现有RSA实现。这样做一遍,后面编码阶段基本不会出现标准遗漏的问题。