问题背景
在我的项目中,须要应用随机数,间接应用 new Random()
效率太低,为了进步随机性和性能,sonar
倡议,应用子类 java.security.SecureRandom
来实现。
问题程序
public class Test {public static void main(String[] args) throws java.security.NoSuchAlgorithmException {byte[] k = new byte[20];
java.security.SecureRandom.getInstanceStrong().nextBytes(k);
System.out.println(java.util.Arrays.toString(k));
}
}
此代码在本地环境 (Win10 + OracleJDK8) 和测试环境(CentOS7 + OpenJDK1.8)均能失常输入后果。然而在云服务器(CentOS7 + OpenJDK1.8)始终阻塞。
问题解析
通过查问材料,发现 SecureRandom.getInstanceStrong()
办法在 Linux
环境下应用 /dev/random
生成随机数的种子。然而 /dev/random
是一个阻塞数字生成器,如果没有足够的随机数据,那么就会始终期待,迫使 JVM 阻塞。
Linux
内核采纳熵来形容数据的随机性,熵(entropy
)是形容零碎凌乱无序水平的物理量,一个零碎的熵越大则阐明该零碎的有序性越差,即不确定性越大。内核保护了一个熵池用来收集来自设施驱动程序和其它起源的环境乐音。
简言之就是 键盘和鼠标的输出 以及 磁盘流动 能够产生所需的随机性或熵。
但在一个不足这样的流动服务器,可能会呈现问题,当零碎的熵池中数量有余时,就会阻塞以后线程。
Linux 的随机数
random
设施了提供了 2 个字符设施供用户态过程应用:
/dev/random
实用于对随机数品质要求比拟高的申请,在熵池中数据有余时,读取/dev/random
设施时会返回小于熵池噪声总数的随机字节。/dev/random
可生成高随机性的公钥或一次性密码本。若熵池空了,对/dev/random
的读操作将会被阻塞,直到收集到了足够的环境噪声为止。这样的设计使得/dev/random
是真正的随机数发生器,提供了最大可能的随机数据熵。/dev/urandom
非阻塞的随机数发生器,它会重复使用熵池中的数据以产生伪随机数据。这示意对/dev/urandom
的读取操作不会产生阻塞,但其输入的熵可能小于/dev/random
的。它能够作为生成较低强度明码的伪随机数生成器,对大多数利用来说,随机性是能够承受的。
解决方案
应用 /dev/urandom
非阻塞的形式来产生随机数,用 new SecureRandom()
代替SecureRandom.getInstanceStrong()
。
public class Test {public static void main(String[] args) {byte[] k = new byte[20];
new java.security.SecureRandom().nextBytes(k);
System.out.println(java.util.Arrays.toString(k));
}
}
注:以上代码片段间接应用命令行编译运行
javac Test.java
java Test