Linux主机熵值不足导致SecureRandom线程阻塞问题

Linux主机熵值不足导致SecureRandom线程阻塞问题

linux操作系统熵值不够,导致使用安全随机数时,长时间线程阻塞。

问题定位

使用jdk远程debug,发现线程堆栈停在SecureRandom.nextBytes()上,出现阻塞状态。表现为程序长时间无法向下运行。

问题根因

jre的安全随机数会使用linux的随机数生成器(在linux上实际上使用的是/dev/random或者/dev/urandom)来生成安全随机数,即提供永不为空的随机字节数据流。默认会使用阻塞算法获取随机数,如果熵值不够则会导致长时间无法获取随机数,导致线程阻塞。

知识扩展

随机数产生的原理

为了尽可能的做到随机,随机数生成器会收集系统环境中各种数据,比如:鼠标的移动,键盘的输入, 终端的连接以及断开,音视频的播放,系统中断,内存 CPU 的使用等等

生成器把收集到的各种环境数据放入一个池子 ( 熵池 ) 中,然后将这些数据进行去偏、漂白,主要目的也是使得数据更加无序,更加难以猜测或者预料得到。

有了大量的环境数据之后,每次获取随机数时,从池子中读取指定的字节序列,这些字节序列就是生成器生成的随机数

随机数生成器结构
环境噪音:鼠标、键盘、中断
收集器
主熵池
次熵池
urandom熵池
/dev/random
/dev/urandom

/dev/random和/dev/urandom都是linux的随机数生成器,但是二者也不相同。/dev/random是阻塞的,当系统熵值不足时会一直阻塞,尝试获取随机数的线程就会等待,直到熵值足够,所以也被称为真随机。而/dev/urandom是非阻塞的,在熵值不够时会产生伪随机数。

  • /dev/random:随机性高,阻塞,真随机
  • /dev/urandom:随机性稍差,非阻塞,可能产生伪随机

因此当有强随机性要求(密码,安全要求)时需要使用/dev/random。

使用SecureRandom一定获取到安全随机数吗?

SecureRandom使用的到底是/dev/random还是/dev/urandom呢?

查看SecureRandom源码

public SecureRandom() {
    /*
         * This call to our superclass constructor will result in a call
         * to our own {@code setSeed} method, which will return
         * immediately when it is passed zero.
         */
    super(0);
    // 默认获取伪随机生成器
    getDefaultPRNG(false, null);
}

如果直接使用SecureRandom random = new SecureRandom()无法保证安全随机,那么如何获取安全随机数呢?SecureRandom提供了方法getInstanceStrong()

public static SecureRandom getInstanceStrong() throws NoSuchAlgorithmException {
    String property = AccessController.doPrivileged(
        new PrivilegedAction<String>() {
            @Override
            public String run() {
                return Security.getProperty(
                    "securerandom.strongAlgorithms");
            }
        });
    .....
}

此方法获取的是/jre/lib/security/java.security配置文件中的securerandom.strongAlgorithms配置的算法,默认配置为阻塞的安全随机算法:

#
# A list of known strong SecureRandom implementations.
#
# To help guide applications in selecting a suitable strong
# java.security.SecureRandom implementation, Java distributions should
# indicate a list of known strong implementations using the property.
#
# This is a comma-separated list of algorithm and/or algorithm:provider
# entries.
#
securerandom.strongAlgorithms=NativePRNGBlocking:SUN

这也是为什么网上很多教程说使用下面方法规避:

#错误方法一:增加JVM参数
-Djava.security.egd=file:/dev/./urandom
#错误方法二:改JRE配置文件 配置文件:$JAVA_HOME/jre/lib/security/java.security
securerandom.source=file:/dev/urandom

但是从getInstanceStrong()方法以及securerandom.strongAlgorithms配置可知,默认仍为阻塞的安全随机算法,不能解决问题。应使用如下解决方案

解决方法

当使用安全随机数时,即使用类似如下代码时,

SecureRandom random = SecureRandom.getInstanceStrong();
random.nextBytes(bytes);

可能会由于系统熵值不够,导致线程长时间等待,假死。

解决方法一:增加linux系统熵值

查看系统熵池的容量

cat /proc/sys/kernel/random/poolsize

查看系统熵池中拥有的熵数:一般为1000以上

cat /proc/sys/kernel/random/entropy_avail

查看从熵池中读取熵的阀值,当 entropy_avail 中的值少于这个阀值,读取 /dev/random 会被阻塞:

cat /proc/sys/kernel/random/read_wakeup_threshold

安装haveged或rngd服务给linux补熵,这里以haveged为例。

安装haveged
yum install haveged
启动命令
systemctl start haveged.service
执行以下命令,开启服务开机自启动。
systemctl enable haveged.service
执行以下命令,查看是否开机启动。
systemctl is-enabled haveged.service
解决方法二:使用伪随机临时规避

只有将securerandom.strongAlgorithms改为非阻塞的算法才能规避此问题,但是也将系统由安全随机改为伪随机,不能用于生产环境

securerandom.strongAlgorithms=NativePRNGNonBlocking:SUN
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值