第二章:国密算法概述(SM2 / SM3 / SM4)

搞国密改造,第一关就是先把这三个算法吃透。SM2、SM3、SM4,说白了就是国标版的“公钥加密+哈希+对称加密”。很多第一次接触的朋友容易把它们跟国际算法混着用,结果互操作性测试一跑就崩。我刚开始做第一个改造项目时也踩过这个坑——SM2的签名值格式跟ECDSA不一样,直接用OpenSSL的ECDSA验签,验一百次失败一百次。

所以这一章,咱不绕弯子,直接把这哥仨的定位、原理、关键参数和常见误区捋清楚。你往后写测试用例、搭验证环境,都会用到这些底子。

2.1 SM2——椭圆曲线公钥密码算法

SM2对标的是ECC(椭圆曲线密码),但曲线参数是自主研发的。嗯,这里要注意:SM2的曲线方程虽然是 y² = x³ + ax + b,但参数 a、b、p、G、n 都是国家指定的一套常量值,不能自己随便改。

我建议你在做互操作性测试时,先确认两件事:

  • 对方用的曲线是不是 SM2 标准曲线(OID 1.2.156.10197.1.301)
  • 签名格式是 DER 编码的 (r, s),还是简单的字节拼接

大部分国密SDK默认输出DER编码,但有些国产密码机偏好裸的64字节 r||s。我曾经因为这个差异在联调时熬了两个通宵,最后发现是签名格式没对齐。

核心参数速查表

参数值备注
曲线长度256 位安全性等价 RSA-3072
基点 G固定值(国标 GM/T 0003)验签时必须使用
密钥长度私钥 32 字节,公钥 65 字节(含前缀04)压缩格式极少用
签名长度DER 编码约 70~72 字节裸格式固定 64 字节
小技巧: 测试时先打印公钥的前两个字节,如果是 04 开头,说明对方没做任何格式转换,直接用了原始点。

2.2 SM3——密码杂凑算法

SM3 就是国标的 SHA-256。输出长度 256 位,消息分组长度 512 位。说实话,它的结构跟 SHA-256 很像——消息扩展、压缩函数、迭代轮数都是 64 轮。但这里有区别:SM3 的初始向量和常数是重新定义的,而且压缩函数里用了一个独立的置换函数 P。

你能用 SM3 的地方:

  • 数字签名的消息哈希(SM2 签名前必须用 SM3)
  • 消息认证码(HMAC-SM3)
  • 基于哈希的随机数生成

我曾遇到一个坑:某密码卡输出的 SM3 摘要长度是 32 字节没错,但它在大端序和小端序上跟软件库不一致。后来发现是底层字节序没转过来。我的建议是——拿到摘要后,先跟开源实现(比如 GmSSL)跑一遍相同的输入对比,别盲目信任对方文档。

简单算个 SM3 的伪代码:

输入: 消息 m (任意长度)
输出: 256位摘要 h

1. 填充: 在 m 后补 1 个 '1',再补 '0',最后附上 64 位长度
2. 分组: 每 512 位为一个分组
3. 迭代压缩:
   每组做 64 轮扩展(W0~W67,W'0~W'63)
   每轮更新 8 个寄存器 (A~H)
4. 输出串联: A||B||C||D||E||F||G||H
注意: 虽然 SM3 和 SHA-256 输出长度相同,但摘要值完全不同。不要试图用 SM3 摘要去调用一个只支持 SHA-256 的签名接口,互操作性测试的第一步就是检查哈希算法标识。

2.3 SM4——分组对称密码算法

SM4 对标的是 AES-128。密钥长度 128 位,分组长度也是 128 位。加密模式上,它支持 ECB、CBC、CTR、GCM 等常见模式。不过你可能觉得奇怪——为什么偏偏是 128 位?因为国密设计时认为 128 位在安全性和性能上已经足够,而且 SM4 的轮数只有 32 轮,比 AES 的 10/12/14 轮还多,看起来更保守。

实际项目中,我最常用的是 SM4-CBC 和 SM4-GCM。给你个对比:

模式优点缺点适用场景
SM4-CBC实现简单,几乎所有密码设备都支持不能并行,需要 IV通用数据加密(文件、数据库)
SM4-GCM认证加密,防止篡改实现复杂,部分老密码机不支持通信协议(TLS、IPsec)

我记得有一次测试 SM4-CBC 加解密,发现用同一个密钥和 IV,不同平台加出来的密文竟然差几个字节。查到最后,原来是 PKCS#7 填充方式不同——有的平台缺省用全0填充,有的用标准 PKCS7。互操作性测试里,填充方式必须明确写在测试报告里。

2.4 三者的协作关系

你现在应该能想象到一个典型的国密应用流程了:

  1. 发送方用 SM3 计算消息摘要
  2. 用 SM2 私钥签名摘要,生成签名值
  3. 用 SM4 加密整个消息(或者用 SM4 密钥交换后加密通信)
  4. 接收方用 SM4 解密,用 SM3 重新算摘要,用 SM2 公钥验签

这种“SM3+SM2+SM4”的组合,就是国密体系的基本模型。当然,实际协议里还要处理密钥协商、身份认证等环节,但核心算法就是这三个。

整个关系的逻辑我画了张图,帮你快速建立框架:

国密算法核心关系图 SM2 公钥密码 数字签名 / 密钥交换 SM3 哈希算法 消息摘要 / 完整性 SM4 对称密码 加密 / 解密 摘要输入 HMAC 互操作性测试关注点 签名:SM3(消息) → SM2(签名) 加密:SM4(消息) 配合 SM2 进行密钥协商

2.5 互操作性测试中的常见误区和检查点

说完了原理,我给你列几条在实际测试中容易翻车的地方。这些是我一个个项目摔出来的:

  • 算法 OID 不匹配:SM2 签名的 OID 是 1.2.156.10197.1.301,SM3 摘要的 OID 是 1.2.156.10197.1.401,SM4 的 OID 是 1.2.156.10197.1.104。测试时务必将 OID 作为必测项目。
  • 字节序问题:SM2 签名值 (r, s),有些平台输出大端序,有些输出小端序。SM3 摘要全是大端序,但不同语言实现可能在内存中存储为小端序,需要转换。
  • 填充模式:SM4 的 ECB/CBC 常用 PKCS7 填充。如果一方用了无填充或 ANSI X.923,另一方就必须对应校验。建议在测试用例中显式写明填充模式。
  • 密钥编码:SM2 公钥常用 04||X||Y 格式(65字节),私钥是 32 字节的原始值。但有些密码机输出的是 DER 编码的 SubjectPublicKeyInfo,你又得多做一步解析。
值得记住: 互操作性测试不是跑通一次就完事。你要建一个“参数矩阵”——覆盖 OID、字节序、填充模式、签名格式的全部组合。我自己常用的矩阵是 4×2×2×2 = 32 个用例,跑一轮基本就能暴露所有互操作问题。

2.6 补充两个实用工具

平时做快速验证,我会用以下两个小工具(虚拟示例,无需实际下载):

  • GmSSL 命令行:gmssl sm2sign -in msg.bin -out sig.der,支持所有国密算法,且开源。
  • Python pysmx 库:轻量级国密实现,适合写自动化测试脚本。

你想想看,如果连开源库和自己写的测试代码都串不通,那跟第三方设备联调就更不用说了。所以我的做法是:先拿 GmSSL 作为“黄金参考”,把所有测试向量跑通,再用它来验证待测密码模块的输出。

好了,这章内容就到这里。SM2、SM3、SM4 的原理和互操作性要点你已经清楚了。后面几章我们会围绕这三个算法,深挖具体的测试流程和工具链。你先把这张图印在脑子里,下一步就好走了。