3. SM2 密钥生成:从原理到落地

密钥生成,是所有密码学应用的起点。SM2 的密钥对生成,说白了就是选一个大整数作为私钥,再算一个椭圆曲线上的点作为公钥。听起来简单,但这里面的坑,我踩过不少。

我个人习惯把密钥生成拆成三个环节来看:原理、随机数、存储。任何一个环节出问题,整个安全体系就白搭了。

3.1 密钥对生成原理

SM2 的密钥对生成,基于椭圆曲线离散对数难题。具体流程是这样的:

  1. 选择一个随机整数 d,范围在 [1, n-2] 之间。n 是 SM2 曲线的阶。
  2. 计算公钥 P = d * G,G 是椭圆曲线的基点。
  3. 私钥就是 d,公钥就是 P。

嗯,这里要注意:d 不能取 0,也不能取 n-1。虽然概率极低,但规范里明确写了,咱们得遵守。

核心公式:

私钥 d ∈ [1, n-2]
公钥 P = d * G (椭圆曲线标量乘法)

我在项目中遇到过一个问题:有人直接用 OpenSSL 的随机数生成私钥,但没检查范围。结果有一次测试,私钥恰好是 0,整个签名验证全挂了。从那以后,我每次生成密钥对,都会加一个范围校验。

3.2 安全随机数生成器

随机数生成器,是密钥生成的命门。你想想看,如果随机数能被预测,那私钥就等于公开了。

SM2 密钥生成对随机数的要求很高。我一般把它分成两类:

类型 全称 特点 典型实现
TRNG 真随机数生成器 基于物理噪声,不可预测 芯片内建的 ADC 噪声、环形振荡器
DRBG 确定性随机数生成器 基于种子,可重复,需熵源 HMAC_DRBG、CTR_DRBG

TRNG 是根,DRBG 是干。 我习惯的做法是:用 TRNG 给 DRBG 提供种子,然后用 DRBG 生成私钥。这样既保证了不可预测性,又保证了可重复性(调试时很有用)。

我的经验: 在嵌入式设备上,TRNG 的熵源质量参差不齐。我曾经遇到过一款芯片,它的 TRNG 输出有明显的周期性。后来我加了一个健康测试,实时监测 TRNG 输出的熵值,低于阈值就报警。

DRBG 的选择上,我个人推荐 HMAC_DRBG。为什么?因为它抗侧信道攻击的能力更强。CTR_DRBG 虽然快,但在某些硬件实现上,容易泄露中间状态。

3.3 密钥存储与保护策略

密钥生成完了,怎么存?这是个大问题。很多安全漏洞,都出在存储环节。

我把它分成三个层次:

  • 软件层: 密钥不能明文存在内存里。用完后立即清零。
  • 硬件层: 使用安全元件(SE)或 TEE 来存储私钥。
  • 传输层: 密钥在芯片间传输时,必须加密。

警告: 千万不要把私钥硬编码在代码里!我曾经审计过一个项目,他们把测试用的私钥直接写在源码中,还提交到了 Git 仓库。结果可想而知,整个产品线都得回炉。

具体到 SM2 私钥的存储,我建议这样做:

  1. 私钥生成后,立即写入安全存储区(如 OTP 或 eFuse)。
  2. 内存中的私钥副本,用完后用 memset_s 清零。注意,不能用普通的 memset,编译器可能会优化掉。
  3. 如果必须导出私钥,使用 SM4 加密后再传输。

公钥的存储相对宽松,但也要防篡改。我一般会给公钥加一个哈希校验,每次使用前验证一下。

3.4 密钥生成流程图

下面这张图,是我自己总结的 SM2 密钥生成流程。你看一眼,基本就全明白了。

SM2 密钥对生成流程图 TRNG 熵源 DRBG 种子生成 私钥 d ∈ [1, n-2] 公钥 P = d * G 安全存储(SE/TEE) 关键检查点 • 私钥范围校验 • 公钥有效性验证 • 随机数健康测试

这张图里,我特意把 TRNG → DRBG → 私钥 → 公钥 → 存储 这条主线画出来了。每个环节都有对应的检查点,缺一不可。

3.5 避坑指南

做 SM2 密钥生成,有几个坑我不得不提:

  • 随机数重复: 我曾经遇到过,设备上电后 TRNG 输出的初始值是一样的。原因是硬件初始化没做好。解决方案是:上电后先读一批随机数扔掉,等熵源稳定了再用。
  • 私钥导出: 有些场景需要备份私钥。我建议用 SM4 加密后导出,密钥派生使用 PBKDF2。千万别直接传明文。
  • 公钥压缩: SM2 公钥可以压缩,但压缩后的公钥需要额外校验。我一般用未压缩格式,省心。

小技巧: 在调试阶段,可以用固定的测试向量来验证密钥生成逻辑是否正确。SM2 标准附录里就有示例,直接拿来用就行。

好了,SM2 密钥生成这块,核心就是这些。记住:随机数要真,私钥要藏,公钥要验。做到这三点,基本就稳了。