## 国密算法标准解读

RESTful接口国密封装教程 - 第3节

国密算法,到底怎么定义?

做接口加密时,很多人上来就问:“国密算法到底是什么?跟国际算法有啥区别?”我刚开始接触时也懵过。其实国家密码管理局(简称“国密局”)从2010年左右就开始系统性发布一系列密码行业标准,后来逐步升级为国家标准(GB/T 开头)。这套算法体系,说白了就是咱们自己的“密码工具箱”。

嗯,这里面有哈希算法、分组密码、公钥密码,还有标识密码。名字你可能听过:SM2、SM3、SM4、SM9。每个算法都有对应的标准文档,定义了具体的参数、运算规则、接口格式。做RESTful接口封装时,你必须严格按照这些标准来,否则合规过不了,甚至系统间互不认。

标准体系一览

先看一张总览图,帮你快速建立整体认知:

国密算法标准体系结构 RESTful API 安全通信层 数字签名(SM2) 密钥协商(SM2) 数据加密(SM4/SM2) SM3 杂凑算法 256位输出 SM4 分组密码 128位密钥 SM2 椭圆曲线 256位素域 SM9 标识密码(无证书公钥) 说明:SM3 为 SM2 和 SM9 提供哈希支撑;SM4 纯对称加解密;SM2 可作签名、加密、密钥交换;SM9 利用用户标识替代证书 在实际 API 封装中,SM2 签名 + SM4 加密是最常见的组合。

一个实用小经验:我在项目中遇到过客户只要求“国密合规”,但对具体用哪个算法并无概念。这时候我一般建议:身份认证用 SM2 签名,数据加密用 SM4,完整性校验用 SM3。这个组合能覆盖大部分 RESTful 接口的安全需求。

SM3 杂凑算法:国标里的“SHA-256”

先聊 SM3。它的标准编号是 GB/T 32905-2016。我个人习惯把它叫作“国产 SHA-256”,因为输出长度也是 256 位(32 字节),但内部结构不一样。

核心参数

参数项值
输出长度256 bits (32 bytes)
消息分组长度512 bits (64 bytes)
字长32 bits
迭代轮数64 轮
安全强度128 bits(抗碰撞)
标准文档GB/T 32905-2016

你想想看,SM3 跟 SHA-256 比,主要区别在于消息扩展算法和压缩函数的布尔函数设计。我在一次合规审查中做过对比测试,同样长度的消息,SM3 运算比 SHA-256 慢大约 10%~15%,但完全能接受。而且 SM3 的置换函数对某些攻击路径有更强的抵抗。

注意:不要直接把 SHA-256 的哈希值拿来“宣称”国密合规。我曾经遇到一个项目,开发觉得“反正都是 256 位,先上线再改”,结果被监管点名整改。必须使用标准库中的 SM3 实现,不能自行“映射”。

SM4 分组密码:对称加密的国标选择

SM4 对应的标准是 GB/T 32907-2016。它是分组密码,分组长度 128 位,密钥长度也是 128 位。我最早接触 SM4 是在替换 DES/3DES 的政府项目中,后来发现它用在 RESTful 接口的数据加密上非常顺手。

核心参数

参数项值
分组大小128 bits
密钥长度128 bits
轮数32 轮
密钥扩展算法32 轮非线性迭代
安全强度128 bits
标准文档GB/T 32907-2016

工作模式与填充

SM4 本身只是一个加密算法,具体怎么用还要搭配工作模式。RESTful 接口中最常用的是 ECB 和 CBC,但 ECB 模式不安全,我建议优先用 CBC 模式 + PKCS7 填充。如果你需要并行加密或流式加密,可以考虑 CTR 模式。

避坑指南:我曾经在接口联调时发现两边 SM4 加密结果一直对不上,折腾半天才发现一方的 IV 写死了全零,另一方用的随机 IV。国标中没有规定 IV 必须随机还是固定,但为了安全,我建议每次加密都生成新的随机 IV,并作为密文的一部分传给对方。

SM2 椭圆曲线公钥密码:签名、加密、密钥交换

SM2 是一套公钥密码体系,包含数字签名算法(SM2-2)、公钥加密算法(SM2-3)和密钥交换协议(SM2-4)。标准编号 GB/T 32918.1~5。它使用 256 位素域上的椭圆曲线,安全强度与 3072 位 RSA 相当。

关键参数

参数项值
曲线方程y² = x³ + ax + b,p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
基点 G 坐标(Gx, Gy)由标准定义
阶 n略小于 2²⁵⁶
签名长度64 bytes(r, s 各 32)
密文长度明文长度 + 97 bytes(SM2 混合加密)
标准文档GB/T 32918.1~5

我个人习惯:在 RESTful API 中,SM2 主要用于两个地方:一是客户端对请求参数做数字签名,服务端验签;二是双方用 SM2 密钥交换协商出一个临时会话密钥,然后用 SM4 加密后续通信。这样既能解决身份认证,又能保证数据机密性。

SM9 标识密码:不需要证书的公钥体系

SM9 标准编号 GB/T 38635.1~2。它的特点是直接用用户标识(比如手机号、邮箱)作为公钥,省去了证书管理和 PKI 基础设施。实现上基于双线性对(配对运算),计算量比 SM2 大。

我平时不主张在内部 RESTful API 中一上来就用 SM9,因为它的运算速度是 SM2 的 3~5 倍,对接口延时影响明显。但如果是开放平台,需要大量用户、又不方便托管证书的场景,SM9 就是一个很优雅的解法。

算法参数对比总表

算法标准编号密钥/输出长度分组/消息块算法类型主要用途
SM3GB/T 32905256 bits512 bits杂凑完整性校验、数字签名基础
SM4GB/T 32907128 bits128 bits对称分组密码数据加密
SM2GB/T 32918256 bits(私钥)—椭圆曲线公钥密码签名、加密、密钥交换
SM9GB/T 38635无固定(基于标识)—标识密码(双线性对)无证书签名/加密

小结:选型思路

做 RESTful 接口封装时,我的默认选型是这样:

  1. 不要自己实现算法 —— 使用经过国密局认证的密码库(如 GmSSL、腾讯的 Tencrypt、华为的 SecC)。
  2. 签名 + 完整性:SM2 签名 + SM3 哈希。签名前先对参数按字典序排序、拼接、SM3 摘要。
  3. 加密传输:SM4-CBC-PKCS7,密钥用 SM2 密钥交换或预置。IV 随机,附在消息头里。
  4. 密钥长度必须严格按标准,不要自作聪明“改小”或“改大”。

特别提醒:SM2 公钥格式有 ASN.1 和原始坐标两种编码方式,接口对接前一定要确认双方用的格式一致。我遇到过因为一方用 DER 编码、一方用未编码的 64 字节串,导致联调了整整两天才发现。