密钥生成安全:TRNG、PRNG 与熵源评估
密钥生成,是整个密钥管理系统的起点。起点要是歪了,后面再怎么加固也没用。我见过不少项目,密钥算法选得挺强,结果随机数发生器是个玩具,最后整个系统形同虚设。今天咱们就把这块掰开揉碎了讲清楚。
真随机数生成器(TRNG)与伪随机数生成器(PRNG)
说白了,TRNG 是从物理世界「薅」随机性,PRNG 是从数学公式「算」随机性。两者各有各的用场,但绝对不能混为一谈。
核心区别一句话:TRNG 不可预测,但速度慢、依赖硬件;PRNG 速度快、可重复,但种子一旦泄露就全完蛋。
TRNG:物理熵源才是真随机
TRNG 依赖物理过程——比如热噪声、时钟抖动、量子效应。我参与过一个金融级 HSM 项目,用的就是基于环形振荡器抖动采样的 TRNG。当时测试了整整两周,确保它在极端温度下依然能输出不可预测的比特流。
常见的 TRNG 实现方式:
- 热噪声放大:利用电阻或二极管的热噪声,放大后采样。成本低,但容易受温度影响。
- 环形振荡器:多个反相器首尾相连,利用相位抖动。我在 FPGA 上做过,实现简单,但需要后处理去偏。
- 量子效应:比如光子分束器、隧穿效应。安全性最高,但硬件贵,目前主要用于国家级或高安全场景。
我的经验:TRNG 输出往往有偏置(0 和 1 比例不均),一定要接一个「去偏算法」——比如 von Neumann 校正器。我曾经踩过这个坑,直接拿原始比特流当密钥,结果统计测试没通过。
PRNG:算法驱动的确定性序列
PRNG 不产生真正的随机性,它只是把种子(seed)扩展成看起来随机的序列。你想想看,只要种子一样,序列就完全一样。这在调试时是好事,但在密钥生成时就是灾难。
常用的密码学安全 PRNG:
- /dev/urandom(Linux):混合了 TRNG 熵池和 CSPRNG,日常够用。
- NIST SP 800-90A 系列:包括 CTR_DRBG、Hash_DRBG、HMAC_DRBG。我推荐 HMAC_DRBG,抗分析能力强。
- Yarrow / Fortuna:FreeBSD 和 macOS 在用,设计上考虑了熵池管理。
注意:绝对不要用 rand()、random()、Math.random() 这类非密码学 PRNG 生成密钥。它们的设计目标是统计均匀,不是安全不可预测。我曾经审计过一个 IoT 项目,他们用 C 标准库的 rand() 生成设备密钥,结果我写了个脚本,几分钟就预测出了所有密钥。
密钥生成算法:AES、RSA、ECC
算法不同,密钥生成的方式也完全不同。我习惯把它们分成两类:对称密钥生成和公私钥对生成。
| 算法 | 密钥类型 | 生成方式 | 典型长度 |
|---|---|---|---|
| AES | 对称密钥 | TRNG 直接输出随机字节 | 128 / 192 / 256 bit |
| RSA | 非对称(公钥+私钥) | 大素数生成 + 模运算 | 2048 / 3072 / 4096 bit |
| ECC | 非对称(公钥+私钥) | 随机标量 + 基点倍乘 | 256 / 384 / 521 bit |
AES 密钥生成
AES 密钥就是一段随机字节。128 位就是 16 字节,256 位就是 32 字节。直接从 TRNG 读出来就行。嗯,这里要注意:生成后一定要做一次「密钥完整性校验」,比如写入后再读出来比对。我遇到过硬件 TRNG 偶尔丢位的情况,不做校验就会出大问题。
RSA 密钥生成
RSA 生成要复杂得多。核心是找两个大素数 p 和 q。流程大致是:
- 用 TRNG 生成一个候选奇数。
- 用 Miller-Rabin 素性测试判断是否为素数(通常迭代 64 轮以上)。
- 找到 p 和 q 后,计算 n = p × q,φ(n) = (p-1)(q-1)。
- 选公钥指数 e(常用 65537),计算私钥 d ≡ e⁻¹ mod φ(n)。
避坑指南:我曾经见过一个实现,p 和 q 太接近,导致 n 容易被 Fermat 分解。建议 p 和 q 的比特长度差至少 100 位以上。另外,生成完一定要检查 p-1 和 q-1 是否包含小因子,否则容易受到 Pollard's p-1 攻击。
ECC 密钥生成
ECC 密钥生成比 RSA 简单。私钥就是一个随机标量 d,范围在 [1, n-1] 之间(n 是椭圆曲线的阶)。公钥就是 d × G,G 是基点。
关键点:
- 随机数 d 必须用 TRNG 生成,且要均匀分布。
- 必须验证公钥是否在曲线上(点验证)。
- 我建议使用 RFC 6979 的确定性 ECDSA,避免因 TRNG 故障导致私钥泄露。
血的教训:2010 年 PlayStation 3 的 ECDSA 签名被破解,就是因为索尼用了固定随机数 k。攻击者直接算出了私钥。所以,随机数质量不是小事,是会直接导致系统被攻破的。
密钥强度与熵源评估
密钥强度不只看长度,更要看熵。128 位的 AES 密钥,如果是从一个只有 32 位熵的种子生成的,那实际强度只有 32 位。你想想看,这跟用 4 位 PIN 码有什么区别?
熵源评估指标
| 指标 | 说明 | 我常用的工具 |
|---|---|---|
| 最小熵(Min-Entropy) | 最坏情况下每个比特携带的熵 | NIST SP 800-90B |
| 自相关 | 检查比特序列是否存在周期性 | dieharder / TestU01 |
| 偏置(Bias) | 0 和 1 的比例是否接近 50% | ent 工具 |
| 压缩比 | 能否被无损压缩 | gzip / bzip2 |
实际评估流程
我在项目中一般按这个步骤来:
- 采集原始数据:从 TRNG 连续采集至少 1M 比特。
- 运行 NIST SP 800-22 测试套件:15 项统计测试,包括频数、游程、FFT 等。
- 估算最小熵:用 SP 800-90B 的估算方法,确认每比特熵不低于 0.9。
- 健康测试:上电自检 + 运行时连续监测。一旦熵源异常,立即告警并停止密钥生成。
我的建议:永远不要假设 TRNG 是完美的。设计时就要考虑「熵源降级」场景——比如 TRNG 硬件故障时,自动切换到混合模式(TRNG + 种子熵池),并记录审计日志。
知识体系总览
下面这张图,是我自己梳理的密钥生成安全知识结构。每次做架构设计时,我都会对着它过一遍,确保没有遗漏。
这张图把整个密钥生成安全的知识点串起来了。从顶层的密钥生成,到 TRNG/PRNG 的选择,再到具体算法的实现,最后落到评估和测试。每个环节都环环相扣。
最后说一句:密钥生成不是「写完代码就完事」的模块。它需要持续监控、定期评估、及时响应。我建议每个季度至少做一次熵源质量审计,把测试结果存档。万一出了安全问题,这就是你自证清白的证据。