securerandom 是密码学场景唯一合规选择,因其对接系统高熵源并满足fips 140-2等标准;普通random因弱熵初始化易被预测,绝不可用于会话密钥生成。

为什么必须用 SecureRandom
会话密钥一旦可预测,整个通信链路就形同裸奔。java.util.Random 基于线性同余算法,仅靠系统时间等弱熵初始化,攻击者在知道部分输出或大致时间范围后,就能反推全部密钥序列。SecureRandom 则强制对接操作系统高熵源(如 Linux 的 /dev/urandom、Windows 的 BCryptGenRandom),并满足 FIPS 140-2 和 RFC 4086 的非确定性与统计随机性要求。
生成 AES 会话密钥的标准流程
以最常用的 128 位 AES 密钥为例:
- 创建 KeyGenerator 实例,指定算法为
"AES" - 调用
init()方法,传入密钥长度(128)和一个 SecureRandom 实例 - 执行
generateKey()得到 SecretKey 对象
代码示例:
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("NativePRNG", "SUN"); // Linux/macOS 推荐
keyGen.init(128, secureRandom);
SecretKey sessionKey = keyGen.generateKey();
关键配置细节不能跳过
算法名和提供程序的选择直接影响熵源质量:
- 优先使用
"NativePRNG"(Linux/macOS)或"Windows-PRNG"(Windows),它们直连系统熵池,不依赖 Java 自带的 SHA1PRNG 等软件 PRNG - 避免只写
SecureRandom.getInstance("SHA1PRNG"),该实现可能回退到低熵种子,且不同 JDK 版本行为不一致 - 绝对不要调用
setSeed()—— 这会覆盖高熵初始状态,引入可预测性 - 不要复用同一个 SecureRandom 实例长期生成大量密钥而不重播种(虽 NativePRNG 本身支持自动重播种,但明确不干预更稳妥)
配套安全实践
密钥生成只是起点,还需配合其他环节:
- 会话密钥应配合随机 IV 使用(如 AES/GCM 模式),IV 也必须用同一 SecureRandom 生成
- 密钥不得硬编码、不得记录日志、不得以明文形式跨进程传递
- 若需持久化,应加密存储(如用 Android Keystore 或 Java KeyStore 封装)
- 测试阶段如需可重现结果,仅限本地环境用固定字节数组构造:
new SecureRandom(new byte[]{1,2,3}),上线前必须删除










