类SecureRandom的构造方法主要是用来定义随机数生成器,常用的构造方法有以下两种。
(1)SecureRandom()
构造一个实现默认随机数算法的安全随机数生成器(RNG)。不带参数比较常用,它使用内置的随机源作为种子,这样每次生成的随机数可以是不同的。
(2)SecureRandom(byte[] seed)
用一个种子seed来构造一个实现默认随机数算法的安全随机数生成器(RNG)。如果seed每次都一样,那么生成的随机数也是每次固定的。
比如下列代码生成一个随机数:
byte[] values = new byte[128]; //构造SecureRandom对象,定义随机数生成器 SecureRandom random = new SecureRandom(); random.nextBytes(values); // 方法nextBytes生成用户指定的随机字节数
所谓随机数种子,就是随机源。第一个构造函数没有人为指定种子(随机源),所以使用系统随机源(取决于$JAVA_HOME/jre/lib/security/java.security配置中的securerandom.source属性)。例如,JDK 1.8中该配置为securerandom.source=file:/dev/random,即从/dev/random获取随机数。第二个构造函数可以人为指定随机源,但是往往用得不太恰当,比如:
byte[] salt = new byte[128]; //使用系统时间作为种子构造随机数发生器 SecureRandom secureRandom = new SecureRandom(System.currentTimeMillis()); secureRandom.nextBytes(salt);
这里指定用当前系统时间作为种子来替代系统默认随机源。如果同一毫秒连续调用,那么得到的随机数是相同的。因此,传递给SecureRandom的种子必须是不可预测的,seed使用不当可能会引发安全漏洞,比如著名的比特币电子钱包漏洞。所以,安全性要求高的场合不要自己指定种子,应当使用系统随机源,一般使用默认的种子生成策略就行,对应Linux里面就是/dev/random和/dev/urandom。其实现原理是:操作系统收集了一些随机事件,比如鼠标单击、键盘敲击等,SecureRandom使用这些随机事件作为种子。