第1章 SM2算法概述

国密算法背景

先说说国密算法是干嘛的。说白了,就是咱们国家自己的一套密码标准。早些年,国内很多系统用的都是国外的算法,比如RSA、ECC这些。但你想,密码这东西可是命门,用别人的算法总归不放心。

我记得在2018年,参与某政务云项目时,甲方明确要求所有敏感数据必须使用国密算法。当时我就在想,这玩意儿得尽早熟悉起来。果不其然,后来金融、电力、医疗等行业陆续出台政策,国密算法几乎成了硬性门槛。

国密算法体系里,有几个关键成员:

  • SM2 — 椭圆曲线公钥密码算法,用于签名和加密
  • SM3 — 密码杂凑算法,类似SHA-256
  • SM4 — 分组密码算法,对标AES

我们今天重点聊的SM2,就是这套体系里的核心。它基于椭圆曲线密码学(ECC),但在具体参数和实现上,跟国际上的ECDSA、ECIES不太一样。

小提示: SM2的密钥长度是256位,安全强度与RSA-3072相当,但计算速度更快。这也是为什么很多高性能场景优先选用SM2。

SM2标准介绍

SM2的标准编号是GM/T 0003-2012,后来更新到了2016版。它包含三个部分:数字签名算法、密钥交换协议、公钥加密算法。我们要讲的分层管理,主要涉及签名和加密这两块。

我习惯把SM2的密钥分成两种角色:

  • 签名密钥 — 用于生成数字签名,验证身份真实性。私钥必须严格保密。
  • 加密密钥 — 用于加密数据,保证机密性。私钥也需保密,但使用场景不同。

你可能会问:既然都是SM2的密钥对,为什么要分开管?我在项目中就遇到过这种情况——直接用同一对密钥又签名又加密。结果呢?签名密钥需要频繁使用,暴露风险高;一旦泄露,加密的数据也全完了。这叫做密钥使用范围混淆,是安全设计的大忌。

警示: 签名密钥和加密密钥必须物理隔离!千万别图省事共用一对。这是我在早期项目里踩过的坑——后来审计时被判定为高风险项,返工了整整两周。

SM2算法的核心

SM2的本质还是椭圆曲线算法。它定义了一条特定的椭圆曲线:

y² = x³ + ax + b

其中参数a和b是固定的。SM2选用的曲线称为sm2p256v1,其参数如下:

参数 值 说明
p FFFFFFFE FFFFFFFF ... (256位) 素数域模数
a FFFFFFFE FFFFFFFF ... (256位) 曲线系数
b 28E9FA9E 9D9F5E34 ... (256位) 曲线系数
G 两个坐标点 (x, y) 基点,用于生成密钥
n FFFFFFFE FFFFFFFF ... (256位) 基点的阶

你可能觉得这些参数看着头疼。没关系,你只需要记住一个核心:密钥对的生成过程。

私钥就是一个随机数d,范围在[1, n-2]之间。公钥就是私钥乘以基点G,即P = d * G。这个计算过程是单向的,从私钥算公钥很容易,但反过来基本不可能(椭圆曲线离散对数问题)。

核心要点: SM2安全性建立在这个“正向易、逆向难”的数学基础上。签名和加密使用同一套数学原理,但密钥的管理策略完全不同。

SM2的密钥架构

既然要分层管理,就得先看看密钥长什么样。SM2密钥对包含:

  • 私钥 — 一个256位的随机整数
  • 公钥 — 一个点坐标 (x, y),各256位,总共512位

公钥的存储格式通常有两种:未压缩格式(完整64字节)和压缩格式(32字节+1个标记位)。

在分层管理时,我习惯把密钥分为三层:

  1. 主密钥(MK) — 根密钥,用于派生其他密钥
  2. 签名子密钥(SK_sig) — 专用于数字签名
  3. 加密子密钥(SK_enc) — 专用于数据加密

为什么要这样分?因为不同密钥的使用频率、安全要求、生命周期都不一样。签名密钥可能每天被调用上千次,而加密密钥可能一年才换一次。混在一起管理,很容易出事。

我的做法: 主密钥用硬件安全模块(HSM)存储,签名子密钥放在业务服务器内存,加密子密钥托管在密钥管理服务(KMS)中。各司其职,互不干扰。
SM2密钥分层架构 主密钥(MK) 种子密钥,用于派生 签名子密钥(SK_sig) 仅用于数字签名 加密子密钥(SK_enc) 仅用于数据加密 接口签名 身份认证 数据传输加密 存储加密 密钥使用范围隔离 签名密钥不用于加密,加密密钥不用于签名

实践中的几点注意

光知道理论不够。我结合项目经验,列几个实际中容易出问题的地方:

  • 密钥长度别搞错 — SM2私钥是256位,不是128位也不是512位。曾经有同事拿SM4的密钥来当SM2用,结果签名验证一直过不去。
  • 随机数质量要保证 — 私钥生成依赖强随机数。别用rand()这种伪随机,建议用硬件随机数或系统提供的加密级随机源。
  • 公钥校验不能省 — 收到对方公钥时,先验证它是否在曲线上,否则可能被注入无效曲线攻击。
我曾经: 在对接第三方接口时,对方传过来的公钥少了一个字节,我没校验就用了。结果运行了三天才被发现,所有签名数据都要重签。从那以后,公钥校验成了我代码里铁打不动的环节。