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/random
或 file:/dev/urandom
,SeedGenerator
会调用平台本地实现。
但 file:/dev/./urandom
这种写法(注意中间的 ./
)会让 JVM 视为一个普通 URL,绕过某些平台特定的行为。
✅ 在 Linux 上,它等价于 /dev/urandom
,并且不会阻塞。
❌ 但在 Windows 上没有这个文件,会导致 JVM 退化到备用机制,初始化可能延迟 5 秒。
6. SecureRandom
的演进历史
随着 Java 版本的演进,java.security.egd
的行为也在变化:
Java 1.4
- 存在
/dev/random
阻塞问题(JDK-4705093)
- 存在
Java 5
- 修复上述问题
- 引入
NativePRNG
,但需手动配置 SHA1PRNG
可能阻塞,除非使用file:/dev/urandom
Java 8
- JEP 123:可配置的安全随机数生成
- 新增
SecureRandom.getInstanceStrong()
- ✅ 不再需要
file:/dev/./urandom
这种绕过方式
Java 9
- JEP 273:基于 DRBG 的
SecureRandom
实现 - 支持 NIST 推荐的确定性随机数生成器
- JEP 273:基于 DRBG 的
📌 了解这些历史有助于你判断当前 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/random
或 file:/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
设备,从宿主机获取熵 - 或者使用
haveged
、rng-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。