密钥生成安全: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。流程大致是:

  1. 用 TRNG 生成一个候选奇数。
  2. 用 Miller-Rabin 素性测试判断是否为素数(通常迭代 64 轮以上)。
  3. 找到 p 和 q 后,计算 n = p × q,φ(n) = (p-1)(q-1)。
  4. 选公钥指数 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

实际评估流程

我在项目中一般按这个步骤来:

  1. 采集原始数据:从 TRNG 连续采集至少 1M 比特。
  2. 运行 NIST SP 800-22 测试套件:15 项统计测试,包括频数、游程、FFT 等。
  3. 估算最小熵:用 SP 800-90B 的估算方法,确认每比特熵不低于 0.9。
  4. 健康测试:上电自检 + 运行时连续监测。一旦熵源异常,立即告警并停止密钥生成。

我的建议:永远不要假设 TRNG 是完美的。设计时就要考虑「熵源降级」场景——比如 TRNG 硬件故障时,自动切换到混合模式(TRNG + 种子熵池),并记录审计日志。

知识体系总览

下面这张图,是我自己梳理的密钥生成安全知识结构。每次做架构设计时,我都会对着它过一遍,确保没有遗漏。

密钥生成安全知识体系 密钥生成安全知识体系 密钥生成 TRNG(物理熵源) PRNG(算法扩展) AES 密钥 RSA 密钥对 ECC 密钥对 其他(SM2等) 密钥强度与熵源评估 最小熵估算 NIST SP 800-22 健康测试 熵源降级处理 核心原则:密钥强度 = 熵源质量 + 算法强度 + 实现正确性 三者缺一不可,任何一环薄弱都会成为攻击突破口

这张图把整个密钥生成安全的知识点串起来了。从顶层的密钥生成,到 TRNG/PRNG 的选择,再到具体算法的实现,最后落到评估和测试。每个环节都环环相扣。

最后说一句:密钥生成不是「写完代码就完事」的模块。它需要持续监控、定期评估、及时响应。我建议每个季度至少做一次熵源质量审计,把测试结果存档。万一出了安全问题,这就是你自证清白的证据。