1. 概述

在启动 Java 虚拟机(JVM)时,我们可以通过设置各种系统属性来调整其行为。其中有一个不太常见但非常关键的参数是 java.security.egd

本文将带你深入了解这个参数的作用、使用方式以及它对 SecureRandom 类的影响。

2. 什么是 java.security.egd

java.security.egd 是一个 JVM 系统属性,用于控制 SecureRandom 类的初始化行为。

和其他 JVM 属性一样,它通过 -D 参数在启动命令中指定:

java -Djava.security.egd=file:/dev/urandom -cp . com.baeldung.java.security.JavaSecurityEgdTester

⚠️ 注意:如果你使用的是 Java 8 或更高版本,并且运行在 Linux 上,JVM 默认已经使用 file:/dev/urandom,所以这个参数可能不会产生明显效果。

3. java.security.egd 有什么作用?

当我们第一次调用 SecureRandom.nextBytes() 时,JVM 会读取配置文件 java.security,其中包含如下配置项:

securerandom.source=file:/dev/random

✅ 安全提供者(如默认的 sun.security.provider.Sun)在初始化时会读取这个配置。

当我们设置了 java.security.egd 后,它可能会覆盖 securerandom.source 的值,从而影响熵源(Entropy Gathering Device, EGD)的选择。

📌 熵源是 SecureRandom 生成随机数时用来获取种子数据的来源。

在 Java 8 及以前,配置文件路径为:

$JAVA_HOME/jre/lib/security/java.security

Java 9 及以后变为:

$JAVA_HOME/conf/security/java.security

⚠️ 但注意:这个参数是否生效,取决于具体的安全提供者的实现。

4. java.security.egd 可以设置哪些值?

常见的取值如下,以 URL 格式表示:

  • file:/dev/random
  • file:/dev/urandom
  • file:/dev/./urandom

这些值在 Unix-like 系统中对应内核提供的熵设备:

  • /dev/random:阻塞式熵源,等待足够熵数据才返回
  • /dev/urandom:非阻塞式熵源,使用已有熵生成伪随机数

📌 不同平台、Java 版本、安全配置下,这些值的效果可能不同。

5. 为什么 file:/dev/./urandom 很特别?

首先我们来区分一下 /dev/random/dev/urandom

特性 /dev/random /dev/urandom
阻塞 ✅ 是 ❌ 否
使用场景 高安全性,如密钥生成 日常加密用途

在 Java 中,如果使用 file:/dev/randomfile:/dev/urandomSeedGenerator 会调用平台本地实现。

file:/dev/./urandom 这种写法(注意中间的 ./)会让 JVM 视为一个普通 URL,绕过某些平台特定的行为。

✅ 在 Linux 上,它等价于 /dev/urandom,并且不会阻塞。

❌ 但在 Windows 上没有这个文件,会导致 JVM 退化到备用机制,初始化可能延迟 5 秒。

6. SecureRandom 的演进历史

随着 Java 版本的演进,java.security.egd 的行为也在变化:

  • Java 1.4

  • Java 5

    • 修复上述问题
    • 引入 NativePRNG,但需手动配置
    • SHA1PRNG 可能阻塞,除非使用 file:/dev/urandom
  • Java 8

    • JEP 123:可配置的安全随机数生成
    • 新增 SecureRandom.getInstanceStrong()
    • ✅ 不再需要 file:/dev/./urandom 这种绕过方式
  • Java 9

    • JEP 273:基于 DRBG 的 SecureRandom 实现
    • 支持 NIST 推荐的确定性随机数生成器

📌 了解这些历史有助于你判断当前 Java 版本下 java.security.egd 是否有效。

7. 实战测试 java.security.egd 的效果

最好的验证方式是跑一下代码。我们写一个测试类:

public class JavaSecurityEgdTester {
    public static final double NANOSECS = 1000000000.0;

    public static void main(String[] args) {
        SecureRandom secureRandom = new SecureRandom();
        long start = System.nanoTime();
        byte[] randomBytes = new byte[256];
        secureRandom.nextBytes(randomBytes);
        double duration = (System.nanoTime() - start) / NANOSECS;

        System.out.println("java.security.egd = " + System.getProperty("java.security.egd") + " took " + duration + " seconds and used the " + secureRandom.getAlgorithm() + " algorithm");
    }
}

然后运行测试:

java -Djava.security.egd=file:/dev/random -cp . com.baeldung.java.security.JavaSecurityEgdTester

输出示例:

java.security.egd=file:/dev/random took 0.692 seconds and used the SHA1PRNG algorithm

继续测试不同值:

java -Djava.security.egd=file:/dev/urandom -cp . com.baeldung.java.security.JavaSecurityEgdTester
java -Djava.security.egd=file:/dev/./urandom -cp . com.baeldung.java.security.JavaSecurityEgdTester
java -Djava.security.egd=baeldung -cp . com.baeldung.java.security.JavaSecurityEgdTester

📌 在 Windows 上,使用 file:/dev/randomfile:/dev/urandom 通常很快,但使用其他值(如 baeldung)会卡 5 秒以上。

8. 使用 SecureRandom.getInstanceStrong() 会怎样?

Java 8 引入了 SecureRandom.getInstanceStrong() 方法,它会返回平台最强的随机数生成器。

我们替换一下代码:

SecureRandom secureRandom = SecureRandom.getInstanceStrong();

再次运行测试:

java -Djava.security.egd=file:/dev/random -cp . com.baeldung.java.security.JavaSecurityEgdTester

📌 在 Windows 上,即使设置 java.security.egd=baeldung,也会快速返回,且算法变为 Windows-PRNG

java.security.egd=baeldung took 0.003 seconds and used the Windows-PRNG algorithm

9. 如何为算法提供种子?

加密中的随机数必须是不可预测的,否则会带来安全隐患。

SecureRandom 通过收集系统熵(如鼠标、键盘输入)来生成种子。

在 Linux 上,熵源通常积累在 /dev/random 中。

❌ Windows 没有 /dev/random 文件。如果设置 -Djava.security.egd=file:/dev/random,默认算法 SHA1PRNG 会使用微软的加密 API 来获取熵。

10. 虚拟机环境下的问题

在虚拟机(VM)中,熵源可能严重不足:

  • VM 没有物理设备(如鼠标、键盘)
  • /dev/random 的熵积累缓慢
  • SecureRandom 初始化可能被阻塞

🔧 解决方案:

  • 在 Red Hat Linux 上,可以配置 virtio-rng 设备,从宿主机获取熵
  • 或者使用 havegedrng-tools 等工具增强熵源

11. 故障排查建议

如果你的应用在生成随机数时卡住,特别是使用了如下技术栈:

  • Spring Boot + 内嵌 Tomcat
  • Tomcat 用 SecureRandom 生成 session ID

⚠️ 当你看到类似日志:

Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [5,123] milliseconds.

📌 立即尝试设置 -Djava.security.egd=file:/dev/./urandom 或其他非阻塞值。

12. 总结

java.security.egd 是一个影响 SecureRandom 行为的 JVM 参数,尤其在 Linux 和低版本 Java 中更为关键。

📌 要点回顾:

  • 它影响熵源的选择
  • 在不同平台、Java 版本中表现不同
  • 虚拟机环境需要特别注意熵源不足问题
  • 使用 getInstanceStrong() 时,该参数可能无效

📚 更多资料:

🛠️ 本文代码已上传至 GitHub


原始标题:The java.security.egd JVM Option