出现这个问题的原因是,Tomcat 需要使用 SecureRandom 类的实例来生成 session ID,这个类的实例是通过调用org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom()
方法来创建的。
在这个过程中,Tomcat 使用了 SHA1PRNG 算法,该算法是基于SHA-1算法实现且保密性较强的伪随机数生成器。
众所周知,伪随机数生成器需要依赖于随机种子(seed)。在 SHA1PRNG 中,有一个种子产生器,它根据配置执行各种操作。
Linux 中的随机数可以从两个特殊的文件设备中产生,一个是/dev/urandom,另外一个是/dev/random。
他们产生随机数的原理是利用当前系统的熵池来计算出固定一定数量的随机bit,然后将这些bit作为字节流返回。
熵池(entropy pool),就是当前系统的环境噪音。
熵,指的是一个系统的混乱程度,系统噪音可以通过很多参数来评估,如内存的使用、文件的使用量、不同类型的进程数量等等。如果当前环境噪音变化得不是很剧烈或者当前环境噪音很小,比如刚开机的时候,而当前需要大量的随机bit,这时产生的随机数的随机效果就不是很好了(也就是说,相对就更有规律可循,也就更容易被攻击)。
Tomcat 依赖的随机数生成器会评估熵池中的噪声数量,因为随机数是从熵池中进行创建的。CentOS 平台 JDK 默认的随机数生成来源于/dev/random。
当读操作时,/dev/random只返回熵池中噪声的随机字节。/dev/random非常适合那些需要非常高质量随机性的场景,比如一次性的支付或生成密钥的场景。
当熵池为空时,来自/dev/random的读操作将被阻塞,直到熵池收集到足够的环境噪声数据。这么做的目的是成为一个密码安全的伪随机数发生器,熵池要有尽可能大的输出。对于生成高质量的加密密钥或者是需要长期保护的场景,一定要这么做。
然而,默认情况下,/dev/random非常慢,因为它只从设备驱动程序和其他(慢)源收集熵。
要解决 Tomcat 因熵池不足而导致的启动阻塞问题,我们可以采取以下几种方案之一:
1、通过 rng-tools 自动补充熵池(推荐)
rng-tools 可以增加内核中的熵量以使其/dev/random更快。rngd允许使用更快的熵源——主要是 TRNG(硬件随机数生成器 ),其存在于诸如 当前的 AMD / Intel 处理器、Via Nano 甚至 Raspberry Pi 等现代硬件中。
我们可以执行下面的Shell命令来安装并启用 rng-tools:
# yum 安装 rng-tools
yum install rng-tools
# 启动 rng-tools
systemctl start rngd
# 设置开机自启
systemctl enable rngd
接着,我们可以通过以下命令查看系统已收集的当前可用的熵的数量是否有所提升(不应该低于 1000)。
cat /proc/sys/kernel/random/entropy_avail
还有一个类似的增加熵量的工具,叫作 Haveged。它基于 HAVEGE 算法。Haveged 可以解决在某些情况下,系统熵过低的问题。然而,它无法保证熵的质量。如果对安全要求较高,应该优先考虑使用硬件随机数生成器 rng-tools。
2、设置Tomcat的启动参数变量
在Tomcat的启动命令行中添加如下片段:
-Djava.security.egd=file:/dev/urandom
# 如果是Java 8 之前的版本,参数值应该为"file:/dev//urandom",因为有 bug
3、修改 JVM 配置文件
将 $JAVA_HOME/jre/lib/security/java.security文件 中的securerandom.source=file:/dev/random改为 securerandom.source=file:/dev/urandom。
注意:出于安全性考虑,推荐使用第一种方式。