第1章 SM2算法概述

做密码学审计这些年,我接触过不少算法标准。SM2这个国密算法,说实话,刚入行时我总觉得它跟国际上的ECDSA差不多,后来深入才明白——细节里全是门道。今天我们先从最基础的「它到底是个什么东西」开始讲。

1.1 国密标准:SM2的来龙去脉

SM2是国家密码管理局发布的椭圆曲线公钥密码算法标准,正式编号是GM/T 0003-2012。它包含三个子协议:数字签名、密钥交换以及公钥加密。嗯,这是国内商用密码体系里的核心组件。

我在项目里见过不少团队,直接把OpenSSL里的ECDSA代码改个曲线参数就当SM2用,结果验签老失败。为什么?因为SM2在签名算法里多了一个公钥验证步骤,计算过程也有差异。标准这东西,差一个字节都不行。

国密标准家族里,SM2通常搭配SM3(密码杂凑算法)和SM4(分组密码算法)一起使用。你可以把它看作中国版的ECC实现,但它并非原样照搬国际标准。

我常用的参考文档:GM/T 0003-2012 全文约40页,重点看第5章(密钥生成)、第6章(签名生成与验证)和第7章(加密解密)。审计时你会发现,算法文档里的附录A列举了推荐的椭圆曲线参数,这个必须跟实际部署对照。

1.2 椭圆曲线密码体制:一个简单理解

椭圆曲线密码(ECC)的核心是一个数学难题:给定椭圆曲线上的两个点,求离散对数很困难。说白了,你知道公钥 P 和基点 G,想反推私钥 d(满足 P = d * G),计算量巨大。

SM2选用的是256位素域上的椭圆曲线,安全性对标RSA-3072。密钥短、性能好,是它的最大优势。

我记得有一次帮客户做密钥生成性能测试,对比过SM2和RSA:SM2生成一对密钥只要几毫秒,RSA-2048要几百毫秒。你想想看,在物联网设备上,这差距就是能不能用的区别。

曲线方程很简单:y² = x³ + ax + b (mod p)。SM2标准里固定了 a、b、p、基点 G 和阶 n。审计时这些参数不能乱改,否则安全性会崩塌。

关键参数表(来自GM/T 0003-2012 附录A)

参数说明SM2推荐值
p素数(256 bit)FFFFFFFE FFFFFFFF ...
a曲线系数FFFFFFFE ... 00000000
b曲线系数28E9FA9E ... 00000001
Gx基点x坐标32C4AE2C ...
Gy基点y坐标BC3736A2 ...
n基点阶(素数)FFFFFFFE ... 7203DF6B

这些值都是16进制表示的,审计时要用工具逐个校验,千万别偷懒。

1.3 密钥对结构:私钥是数,公钥是点

SM2密钥对由两部分组成:

  • 私钥 (d):一个在 [1, n-2] 范围内的随机整数。长度256位,不可预测。
  • 公钥 (P):椭圆曲线上的一个点,满足 P = d * G。用非压缩格式时,包含两个256位的坐标(x, y),总共512位。

我曾在一个项目中遇到公钥存储只保留了x坐标的情况——美其名曰“节省空间”。结果签名验证时程序需要用x去解y,还要判断y的奇偶,增加了计算量不说,万一解出来两个点,代码选错了就完蛋。正规做法是存完整公钥或带标识的压缩格式。

密钥对的生成步骤其实很简单:

  1. 获取系统随机数生成器(真随机或密码学安全伪随机)。
  2. 产生256位随机数 d,检查范围并确保不为0或n-1。
  3. 计算标量乘法 P = d * G。
  4. 输出私钥数据 d 和公钥数据 (x, y)。
# SM2密钥生成伪代码(Python风格)
from sm2 import EC, RandomGenerator

def generate_sm2_keypair():
    rng = RandomGenerator()  # 必须使用国密认证的随机源
    d = rng.random_bits(256)
    while d == 0 or d >= n-1:
        d = rng.random_bits(256)
    P = ec_point_mul(d, G)   # 标量乘法
    return (d, P.x, P.y)

注意:实际代码中,随机数生成器的质量直接影响密钥安全。我曾审计过一个项目,用的rand()函数,导致私钥可预测——那真的是灾难。一定要用硬件随机数或通过国密检测的软件随机源。

知识体系一览

我把这一章的核心知识点画成了结构图,方便你对照记忆。图中从左到右展示了SM2的层次:标准规范→数学体制→密钥对组成→关键参数。

SM2 密钥对知识结构 国密标准 GM/T 0003-2012 椭圆曲线密码体制 (ECC) 256位素域,方程 y² = x³ + ax + b (mod p) 私钥 d 256位随机整数 ∈ [1, n-2] 公钥 P 曲线上点 (x, y) 512位 基点 G 曲线参数 a, b, p 阶 n

图里用虚线连接的底部参数,是椭圆曲线体制的固定值。审计时请对照标准原值,我见过有厂商误用了secp256r1的参数,导致SM2曲线变成国际曲线——标准不匹配,合规审查直接通不过。

密钥对的数据格式

实际存储或传输时,SM2密钥对一般按照ASN.1 DER编码封装。私钥格式引用PKCS#8,公钥格式包含一个标识符 0x04 后接两个256位的大整数。

我用一个简单的表格对比一下常见的格式差异:

格式私钥存储公钥存储典型长度
原始二进制256位512位 (非压缩)32 / 64 字节
PEM (PKCS#8)Base64 + 头尾Base64 + 头尾200~300 字节
DER (X.509)需封装SubjectPublicKeyInfo91 字节左右

我自己的审计习惯:在代码中直接使用原始二进制格式时要格外小心字节序,SM2标准规定大端序(big-endian)。曾经有一个C语言项目用了小端序,结果密钥对完全算不对——找了两天bug才发现。

好了,SM2的基本概念先梳理到这。你会发现,标准、数学、密钥结构这三块是互相咬合的,任何一处理解偏差都会导致后续审计中的漏判。