1.概述
启动Java虚拟机(JVM)时,我们可以定义各种属性,这些属性将改变JVM的行为。这样的属性之一就是java.security.egd.
在本教程中,我们将检查它是什么,如何使用它以及产生什么效果。
2.什么是java.security.egd
?
作为JVM属性,我们可以使用java.security.egd
来影响SecureRandom
类的初始化方式。
像所有JVM属性一样,我们在启动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
读取字节时,我们使它初始化并读取JVM的java.security
配置文件.该文件包含securerandom.source
属性:
securerandom.source=file:/dev/random
安全提供程序(例如默认的sun.security.provider.Sun
在初始化时会读取此属性。
当我们设置java.security.egd
JVM属性时,安全提供程序可以使用它来覆盖securerandom.source
配置的属性。
SecureRandom
生成随机数java.security.egd
和securerandom.source
控制哪个entropy gathering device
(EGD)作为种子数据的主要来源。
在Java 8之前,我们在$JAVA_HOME/jre/lib/security
java.security
,但在以后的实现中,它在$JAVA_HOME/conf/security
。
egd
选项是否有效取决于安全提供程序的实现。
java.security.egd
可以取什么值?
我们可以使用java.security.egd
,其值如下:
-
file:/dev/random
-
file:/dev/urandom
-
file:/dev/./urandom
此设置是否有效,还是其他值有所不同,取决于我们使用的平台和Java版本以及如何配置JVM的安全性。
在基于Unix的操作系统(OS)上, /dev/random
是一个特殊的文件路径,在文件系统中以普通文件的形式出现,但从中读取的内容实际上与OS的设备驱动程序进行交互以生成随机数。一些设备实现还通过/dev/urandom
甚至/dev/arandom
URI提供访问。
5.关于file:/dev/./urandom
有何特别之处?
首先,让我们了解文件/dev/random
和/dev/urandom:
-
/dev/random
从各种来源收集熵;/dev/random
将一直阻塞,直到它具有足够的熵来满足我们的不可预测数据的读取请求为止 -
/dev/urandom
将从可用的任何内容中得出伪随机性,而不会阻塞。
当我们第一次使用SecureRandom
,我们的默认Sun SeedGenerator
初始化。
当我们使用特殊值file:/dev/random
或file:/dev/urandom
,我们使Sun SeedGenerator
使用本机(平台)实现。
Unix上的提供程序实现可能仍通过/dev/random
读取而阻塞。在Java 1.4中,发现某些实现存在此问题。该错误随后在Java 8的JDK增强建议(JEP 123)下得到修复。
file:/dev/./urandom
类的URL或任何其他值,会导致SeedGenerator
将其视为指向我们要使用的种子源的URL 。
在类似Unix的系统上,我们的file:/dev/./urandom
URL解析为相同的非阻塞/dev/urandom
文件。
但是,我们并不总是要使用此值。在Windows上,我们没有此文件,因此我们的URL无法解析。这触发了产生随机性的最终机制,并且可能使我们的初始化延迟约5秒钟。
SecureRandom
的演变
在各种Java版本中java.security.egd
的影响已经改变。
因此,让我们看一些影响SecureRandom
行为的更重要的事件:
- Java 1.4
- 在JDK-4705093下引发的/ dev / random阻止问题:使用/ dev / urandom而不是/ dev / random(如果存在)
- Java 5
- 修复JDK-4705093
- 添加了
NativePRNG
算法以遵守java.security.egd
设置,但我们需要手动对其进行配置 - 如果使用
SHA1PRNG
file:/dev/urandom.
以外的其他任何东西,它可能会阻塞。换句话说,file:/dev/./urandom
,它可能会阻塞
- 添加了
- 修复JDK-4705093
- Java 8
- JEP123:可配置的安全随机数生成
- 添加了新的
SecureRandom
实现,该实现尊重安全属性 - 为平台本地强随机数添加一个新的
getInstanceStrong()
非常适合生成高价值和长期存在的秘密,例如RSA私钥/公钥对 - 我们不再需要
file:/dev/./urandom
解决方法
- 添加了新的
- JEP123:可配置的安全随机数生成
- Java 9
- JEP273:基于DRBG的SecureRandom实现
- 实现了三种确定性随机位生成器(DRBG)机制,如使用确定性随机位生成器生成随机数的建议中所述
- JEP273:基于DRBG的SecureRandom实现
了解SecureRandom
更改方式后,我们可以深入了解java.security.egd
属性的可能效果。
7.测试java.security.egd
要确定JVM属性的效果,最好的方法是尝试一下。因此,让我们通过运行一些代码来创建新的SecureRandom
并定时获取它需要多长时间**java.security.egd
** 一些随机字节。
首先,让我们使用main()
方法JavaSecurityEgdTester
我们将使用System.nanoTime()
secureRandom.nextBytes()
调用,并显示结果:
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实例并为java.security.egd
属性JavaSecurityEgdTester
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
由于我们的系统属性仅在初始化时读取,因此让我们在新的JVM中针对java.security.egd
每个不同值启动类:
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
在使用Java 8或Java 11的Windows上,使用值file:/dev/random
或file:/dev/urandom
进行测试的时间小于2秒钟。使用其他file:/dev/./urandom
,甚至baeldung
,会使我们的测试花费5秒钟以上!
有关为什么会发生这种情况的说明,请参见我们前面的部分。在Linux上,我们可能会得到不同的结果。
8.关于SecureRandom.getInstanceStrong()
呢?
Java 8引入了SecureRandom.getInstanceStrong()
方法。让我们看看这如何影响我们的结果。
首先,让我们用SecureRandom.
new SecureRandom()
getInstanceStrong()
:
SecureRandom secureRandom = SecureRandom.getInstanceStrong();
现在,让我们再次运行测试:
java -Djava.security.egd=file:/dev/random -cp . com.baeldung.java.security.JavaSecurityEgdTester
在Windows上运行时, SecureRandom.getInstanceStrong()
java.security.egd
属性的值没有明显的作用。甚至无法识别的值也可以使我们快速响应。
让我们再次检查输出,注意不到0.01秒的时间。我们还要观察一下,该算法现在是Windows-PRNG:
java.security.egd=baeldung took 0.003 seconds and used the Windows-PRNG algorithm
请注意,算法名称中的PRNG代表伪随机数生成器。
9.播种算法
由于随机数在加密技术中大量用于安全密钥,因此它们必须不可预测。
因此,我们播种算法的方式直接影响它们产生的随机数的可预测性。
为了产生不可预测性, SecureRandom
实现使用从累积的输入中收集的熵来播种其算法。这来自IO设备,例如鼠标和键盘。
在类Unix系统上,我们的熵累积在文件/dev/random
。
Windows上没有/dev/random
文件。将-Djava.security.egd
设置为file:/dev/random
或file:/dev/urandom
会导致默认算法(SHA1PRNG)使用本机Microsoft Crypto API播种。
10.虚拟机如何?
/dev/random
收集很少或没有熵的虚拟机中运行。
虚拟机没有物理鼠标或键盘来生成数据,因此/dev/random
的熵累积的速度要慢得多。这可能会导致我们的默认SecureRandom
调用阻塞,直到有足够的熵使其生成不可预测的数字为止。
我们可以采取一些步骤来减轻这种情况。例如,在RedHat Linux中运行VM时,系统管理员可以配置虚拟IO随机数生成器virtio-rng
。这将从托管它的物理机读取熵。
11.故障排除技巧
如果我们的应用程序或其依赖项在生成SecureRandom
编号时挂起,请考虑java.security.egd
,特别是在我们在Linux上运行以及在Java 8之前的版本上运行时。
我们的Spring Boot应用程序经常使用嵌入式**Tomcat** 。这使用SecureRandom
来生成会话密钥。当我们看到Tomcat的“创建SecureRandom实例”操作花费5秒钟或更长时间时,我们应该为**java.security.egd** .
12.结论
在本教程中,我们学习了JVM属性java.security.egd
是什么,如何使用它以及它具有什么作用。我们还发现,其影响可能会根据我们所运行的平台和所使用的Java版本而有所不同。
最后,我们可以在《 JCA参考指南》和“ SecureRandom API规范SecureRandom
及其工作方式的更多信息,并了解有关urandom的一些神话。
0 评论