Random 不适合加密场景,因其是伪随机数生成器,种子可预测、输出可重现,无法满足IV所需的不可预测性和统计不可区分性;应使用SecureRandom。

不能直接用 Random.nextBytes() 生成加密用的初始化向量(IV)。
为什么 Random 不适合加密场景
Random 是伪随机数生成器(PRNG),基于线性同余法,种子可预测、输出可重现。加密 IV 必须具备不可预测性和统计不可区分性,否则会破坏算法语义安全(比如 CBC 模式下 IV 可预测会导致明文前缀泄露)。
-
Random默认使用System.currentTimeMillis()作种子,时间精度有限且易被枚举 - 同一种子下,
nextBytes()输出完全确定,多线程共享实例时更危险 - JVM 启动后若未显式设种子,不同进程可能生成相似字节序列
应该用 SecureRandom 替代 Random
SecureRandom 是 Java 提供的密码学安全随机数生成器(CSPRNG),底层依赖操作系统熵源(如 Linux 的 /dev/urandom),满足 FIPS 140-2 要求。
- 推荐显式指定算法:优先用
new SecureRandom()(JDK 8+ 默认选用最强实现),或明确指定SecureRandom.getInstance("SHA1PRNG")(注意:该算法在某些旧 Android 版本中存在缺陷,生产环境建议不硬编码) - 避免调用
setSeed()—— 手动设种子反而削弱安全性,除非你有真随机源且理解后果 - 生成 IV 前无需调用
generateSeed()或reseed(),SecureRandom初始化时已自动完成熵收集
示例:
byte[] iv = new byte[16]; // AES-CBC 要求 IV 长度等于块大小(128 bit) new SecureRandom().nextBytes(iv); // 安全,直接可用
IV 使用中的关键细节
生成只是第一步,IV 的传输、存储和复用方式同样决定安全性。
- IV 不需要保密,但必须唯一且不可预测——通常随密文一起传输(如前置在密文前),无需额外加密
- 绝对禁止对同一密钥重复使用相同 IV(尤其是 CBC、CTR 模式),否则等同于裸奔;GCM 模式下重复 IV 会导致密钥彻底失效
- 如果 IV 由协议约定(如 TLS),不要自行生成;若自定义协议,确保 IV 长度匹配算法要求(AES-CBC: 16 字节,ChaCha20-Poly1305: 12 字节)
- 不要从密钥派生 IV(如哈希密钥加计数器),除非使用标准 KDF 并严格遵循规范(如 NIST SP 800-38A 中的 counter mode IV 构造)
最常被忽略的一点:很多人以为“用了 SecureRandom 就万事大吉”,却在多线程环境中复用同一个实例又不做同步,导致内部状态竞争——SecureRandom 实例不是线程安全的(JDK 17 前),应每次新建,或用 ThreadLocal.withInitial(SecureRandom::new) 管理。

















