随机数安全性:Random 与 SecureRandom

伪随机数生成器使用确定的数学算法产生具备良好统计属性的数字序列,但实际上这种数字序列并非具备真正的随机特性。

伪随机数生成器通常以一个种子值为起始,每次计算使用当前种子值生成一个输出及一个新种子,这个新种子会被用于下次计算。

Java 提供的伪随机数生成器 java.util.Random 的不同实例如果使用相同种子值创建,则后续产生的随机数总会重复,如:

@Test
public void test() {
    long seed = System.currentTimeMillis();
    Random random1 = new Random(seed);
    Random random2 = new Random(seed);
    for (int i = 0; i < 10; i++) {
        System.out.println(random1.nextInt() + "=" + random2.nextInt());
    }
}

测试结果:

584397915=584397915
1101630039=1101630039
-1214277036=-1214277036
-119968434=-119968434
-262827225=-262827225
-1539565323=-1539565323
1391920860=1391920860
-922846780=-922846780
2080924613=2080924613
2114543521=2114543521

以上场景也正好模拟了 java.util.Random 的常用场景,使用不带任何参数的 Random 构造函数生成 Random 实例时,系统会使用系统时钟的当前时间作为种子值,那么系统在初始化或重启时生成的 Random 实例的种子值可能都是相同的。攻击者可以在系统中植入监听并构建相应的查询表预测将要使用的种子值,因此安全场景中不能使用 java.util.Random

安全场景中建议使用 java.security.SecureRandomSecureRandom 提供加密的强随机数生成器(RNG),实际上 SecureRandom 继承自 java.util.Random,本质上仍然是伪随机数生成器原理,在种子值确定的情况下生成的随机数也一样,区别在于 SecureRandom 使用的种子值不单单是系统时钟的当前时间,增强了种子值的不可预测性,因此比 Random 安全,不过也因此会带来一定的性能开销,因此 RandomSecureRandom 的选择取决于真实使用场景,安全场景中尽量使用 SecureRandom

java.security.SecureRandom 正确使用示例:

  1. 使用无参构建函数生成 SecureRandom 实例。
@Test
public void test() {
    SecureRandom random1 = new SecureRandom();
    SecureRandom random2 = new SecureRandom();
    for (int i = 0; i < 10; i++) {
        System.out.println(random1.nextInt() + "!=" + random2.nextInt());
    }
}
  1. SecureRandom 内置两种随机数算法:NativePRNGSHA1PRNG,可以指定随机数算法生成SecureRandom 实例。
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");

你可能感兴趣的:(随机数安全性:Random 与 SecureRandom)