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() 函数生成私钥,结果被安全团队直接打回。嗯,这其实是个常识性问题,但压力大的时候确实容易犯。
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 的安全性。
- 获取足够的熵(来自硬件熵源或合格的 DRBG)
- 应用密钥生成算法(直接生成或派生)
- 验证密钥的有效性(如检查是否为无穷远点)
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 需要正确的种子更新机制,否则会出现周期性问题。我曾经在嵌入式设备上踩过这个坑——种子更新不及时,导致生成的密钥序列出现了可预测性。
- 熵源:使用硬件 RNG 提供至少 256 位熵
- 个性化字符串:每个实例使用唯一标识(如设备序列号 + 时间戳)
- 重播种间隔:每生成 2^48 位后强制重播种
2.4 知识体系总览
为了让你更直观地理解这三份标准之间的关系,我画了一张图。你可以把它当作本章的「地图」:
2.5 实际项目中的避坑指南
讲了这么多标准,最后分享几个我在实际项目中遇到的坑,希望能帮你少走弯路。
我曾经接手过一个项目,代码里直接调用了 OpenSSL 的 RAND_bytes(),但没有检查返回值。结果在某个嵌入式平台上,RAND_bytes() 因为熵源不足返回了错误,但代码继续执行,生成了一个全零的密钥对。嗯,后果可想而知。
另一个常见问题是,生成 ECDSA 密钥对后没有验证公钥是否在曲线上。我记得有一次,一个同事生成的公钥竟然是一个无穷远点——虽然概率极低,但确实发生了。FIPS 186-5 明确要求:必须验证公钥的有效性。
- 私钥是否在 [1, n-1] 范围内
- 公钥是否在曲线上(EC_POINT_is_on_curve)
- 公钥是否不是无穷远点
好了,这一章的内容就到这里。记住:密钥生成不是简单的「跑个命令」,它背后有一套完整的标准体系。理解这些标准,你才能写出真正安全的代码。