Experience 10/13/2021
54
0
2

SecurityRandom的getInstanceStrong() 阻塞问题

阻塞 , 随机数 , getInstanceStrong , SecurityRandom
SecurityRandom的getInstanceStrong() 阻塞问题

这两天服务测试上线,然后有一个功能是生成随机六位数作为密码,开发时使用了Securityrandom.getInstanceStrong()这个方法来获取Random对象,在Windows环境下,是没有问题,能够很快获取到六位随机数,但是已发布到centos环境下,就会出现阻塞的问题,这就让人很烦(测试是好的呀,咋就请求超时呢。。。),然后找啊找啊找BUG,找到一个大BUG。。。

相关代码如下:

    content = new StringBuilder();
    Random random = SecureRandom.getInstanceStrong();
    for (int i = 0; i < 5; i++) {
        content.append(random.nextInt(10));
    }

当时也不知道怎么想的,平时都是 new SecurityRandom() ,这次就用了 SecureRandom.getInstanceStrong() 这个方式,一遍遍的看代码,本地测都是正常,偶然间把SecureRandom.getInstanceStrong()复制到百度查了下,哎,有趣的事情发生了。

先了解一些概念和知识

以上描述有几个概念:

  • 熵值:即是随机值的不确定性度量值;
  • 熵源:即是随机数的来源;
  • 熵输入:是伪随机数产生器描述从熵源获取的bit串,用来产生种子;
  • 种子:即是输入到伪随机数产生器用于初始化的bit串;

Random设备了提供了2个字符设备供用户态进程使用:

  1. /dev/random适用于对随机数质量要求比较高的请求,在熵池中数据不足时, 读取/dev/random设备时会返回小于熵池噪声总数的随机字节。/dev/random可生成高随机性的公钥或一次性密码本。若熵池空了,对/dev/random的读操作将会被阻塞,直到收集到了足够的环境噪声为止。这样的设计使得/dev/random是真正的随机数发生器,提供了最大可能的随机数据熵;

  2. /dev/urandom非阻塞的随机数发生器,它会重复使用熵池中的数据以产生伪随机数据。这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。它可以作为生成较低强度密码的伪随机数生成器,对大多数应用来说,随机性是可以接受的;

这个方式在 linux 环境下会发生阻塞的问题,具体原因是:

SecureRandom.getInstanceStrong()方法在Linux环境下使用/dev/random生成随机数的种子。但是/dev/random是一个阻塞数字生成器,如果没有足够的随机数据,那么就会一直等待,迫使 JVM 阻塞。

Linux内核采用熵来描述数据的随机性,熵(entropy)是描述系统混乱无序程度的物理量,一个系统的熵越大则说明该系统的有序性越差,即不确定性越大。内核维护了一个熵池用来收集来自设备驱动程序和其它来源的环境噪音。

简言之就是键盘和鼠标的输入以及磁盘活动可以产生所需的随机性或熵。

但在一个缺乏这样的活动服务器,可能会出现问题,当系统的熵池中数量不足时,就会阻塞当前线程。

Comments