2、密钥生成标准:FIPS 186-5、NIST SP 800-133 与 RNG/DRBG 要求

聊到密钥生成,很多人第一反应就是「openssl genrsa -out key.pem 2048」。嗯,这条命令我用了好多年,但真正让我开始认真研究背后标准的,是一次合规审计。审计员问我:「你用的随机数发生器是哪个?符合哪个标准?」我当时愣住了。从那以后,我养成了一个习惯——每次生成密钥前,先问自己三个问题:算法选对了吗?参数合规吗?随机数够安全吗?

这一章,我们就来拆解这三个问题背后的标准体系。说白了,就是三份文档:FIPS 186-5、NIST SP 800-133 和 RNG/DRBG 相关标准。它们分别管什么?怎么用?有哪些坑?我们一个一个说。

2.1 FIPS 186-5:数字签名算法的密钥生成标准

FIPS 186-5 是美国联邦信息处理标准的最新版本,全称是「Digital Signature Standard (DSS)」。它规定了三种签名算法:DSA、ECDSA 和 EdDSA。每种算法都有对应的密钥生成要求。

我个人习惯把 FIPS 186-5 看作一本「密钥生成说明书」。它告诉你:

  • 密钥长度必须是多少位
  • 域参数(p、q、g 等)怎么选
  • 私钥怎么生成(必须用合格的随机数发生器)
  • 公钥怎么计算

举个例子,ECDSA 的密钥生成流程大致如下:

1. 选择一条椭圆曲线(如 P-256、P-384、P-521)
2. 从域参数中获取基点 G 和阶 n
3. 用 DRBG 生成一个随机整数 d,范围 [1, n-1]
4. 计算公钥 Q = d * G
5. 输出私钥 d 和公钥 Q

这里有个关键点:随机数 d 必须由经认证的 DRBG 生成。我在项目中遇到过有人直接用 rand() 函数生成私钥,结果被安全团队直接打回。嗯,这其实是个常识性问题,但压力大的时候确实容易犯。

⚠️ 避坑提醒: 我曾经见过一个项目,开发人员为了「提高性能」,在生成 ECDSA 密钥时复用了同一个随机数。结果呢?两个私钥被直接推导出来。记住:每次生成密钥都必须使用全新的随机数,这是 FIPS 186-5 的硬性要求。

2.2 NIST SP 800-133:密钥生成与管理的规范

如果说 FIPS 186-5 是「说明书」,那 NIST SP 800-133 就是「操作手册」。它全称是「Recommendation for Key Generation」,专门讲密钥怎么生成、怎么派生、怎么传输。

SP 800-133 把密钥生成分成了两类:

类型 说明 典型场景
直接生成 用 DRBG 直接输出密钥比特 对称密钥(AES、HMAC)
派生生成 从已有密钥材料通过 KDF 派生 会话密钥、子密钥

我个人觉得,SP 800-133 最有价值的部分是它对「密钥生成熵源」的要求。它明确说了:熵源的最小熵必须 ≥ 密钥长度。举个例子,生成 256 位 AES 密钥,熵源必须提供至少 256 位的熵。你想想看,如果熵源只有 128 位熵,那密钥强度实际上只有 128 位,白白浪费了 AES-256 的安全性。

📌 核心要点: SP 800-133 要求密钥生成过程必须包含以下三个步骤:
  1. 获取足够的熵(来自硬件熵源或合格的 DRBG)
  2. 应用密钥生成算法(直接生成或派生)
  3. 验证密钥的有效性(如检查是否为无穷远点)

2.3 随机数生成器:RNG 与 DRBG 的要求

随机数生成器是整个密钥生成体系的「心脏」。没有好的随机数,再强的算法也是白搭。我记得有一次做安全评估,发现某个设备的 RSA 密钥对竟然有重复——原因就是它的随机数生成器坏了,一直输出同一个值。

NIST 把随机数生成器分为两类:

  • RNG(Random Number Generator):硬件随机数发生器,依赖物理过程(如热噪声、量子效应)
  • DRBG(Deterministic Random Bit Generator):确定性随机数发生器,基于密码学算法

DRBG 的标准是 NIST SP 800-90A,它定义了三种机制:

机制 底层算法 典型输出长度
Hash_DRBG SHA-256 / SHA-512 任意长度
HMAC_DRBG HMAC-SHA256 / HMAC-SHA512 任意长度
CTR_DRBG AES-128 / AES-256 任意长度

你可能会问:这三种选哪个好?我个人习惯用 CTR_DRBG,因为它速度快、安全性经过充分验证。但要注意,CTR_DRBG 需要正确的种子更新机制,否则会出现周期性问题。我曾经在嵌入式设备上踩过这个坑——种子更新不及时,导致生成的密钥序列出现了可预测性。

💡 实用建议: 在实际项目中,我通常这样配置 DRBG:
  • 熵源:使用硬件 RNG 提供至少 256 位熵
  • 个性化字符串:每个实例使用唯一标识(如设备序列号 + 时间戳)
  • 重播种间隔:每生成 2^48 位后强制重播种
这样做的好处是,即使某个环节出问题,也不会影响全局安全性。

2.4 知识体系总览

为了让你更直观地理解这三份标准之间的关系,我画了一张图。你可以把它当作本章的「地图」:

密钥生成标准体系结构图 密钥生成标准体系 FIPS 186-5 数字签名标准 NIST SP 800-133 密钥生成与管理 SP 800-90A DRBG 标准 FIPS 186-5 核心内容 • DSA / ECDSA / EdDSA • 密钥长度与域参数 • 私钥随机性要求 SP 800-133 核心内容 • 直接生成 vs 派生生成 • 熵源最小熵要求 • 密钥有效性验证 DRBG 三种机制 • Hash_DRBG (SHA-2) • HMAC_DRBG (HMAC-SHA) • CTR_DRBG (AES) 安全密钥对(私钥 + 公钥) 符合 FIPS 186-5 + SP 800-133 + DRBG 要求 📌 三者关系:FIPS 186-5 规定「生成什么」,SP 800-133 规定「怎么生成」,SP 800-90A 规定「用什么生成」。 缺少任何一个环节,密钥的安全性都无法得到保证。

2.5 实际项目中的避坑指南

讲了这么多标准,最后分享几个我在实际项目中遇到的坑,希望能帮你少走弯路。

⚠️ 坑一:DRBG 未正确播种
我曾经接手过一个项目,代码里直接调用了 OpenSSL 的 RAND_bytes(),但没有检查返回值。结果在某个嵌入式平台上,RAND_bytes() 因为熵源不足返回了错误,但代码继续执行,生成了一个全零的密钥对。嗯,后果可想而知。
💡 解决方案: 每次调用 DRBG 生成随机数后,必须检查返回值。如果返回错误,要立即终止密钥生成流程,并记录日志。这是 SP 800-133 的明确要求。
⚠️ 坑二:密钥生成后未验证有效性
另一个常见问题是,生成 ECDSA 密钥对后没有验证公钥是否在曲线上。我记得有一次,一个同事生成的公钥竟然是一个无穷远点——虽然概率极低,但确实发生了。FIPS 186-5 明确要求:必须验证公钥的有效性。
💡 解决方案: 生成密钥对后,至少做以下验证:
  • 私钥是否在 [1, n-1] 范围内
  • 公钥是否在曲线上(EC_POINT_is_on_curve)
  • 公钥是否不是无穷远点

好了,这一章的内容就到这里。记住:密钥生成不是简单的「跑个命令」,它背后有一套完整的标准体系。理解这些标准,你才能写出真正安全的代码。