java.util.Random和java.security.SecureRandom之间的区别

时间:2012-06-15 13:04:42

标签: java random cryptography security

我的团队交出了一些生成随机令牌的服务器端代码(用Java),我对此有一个问题 -

这些令牌的目的是相当敏感的 - 用于会话ID,密码重置链接等。所以他们确实需要加密随机,以避免有人猜测它们或暴力强制它们可行。令牌是“长”的,所以它是64位长。

代码当前使用java.util.Random类来生成这些令牌。 java.util.Random的文档([http://docs.oracle.com/javase/7/docs/api/java/util/Random.html][1])清楚地说明了以下内容:

  

java.util.Random的实例不具有加密安全性。相反,请考虑使用SecureRandom来获取加密安全的伪随机数生成器,以供安全敏感的应用程序使用。

但是,代码当前使用java.util.Random的方式是这样的 - 它实例化java.security.SecureRandom类,然后使用SecureRandom.nextLong()方法获取用于实例化{的种子{1}}类。然后它使用java.util.Random方法生成令牌。

现在我的问题 - 考虑到使用java.util.Random.nextLong()播种java.util.Random,它仍然不安全吗?我是否需要修改代码才能使用java.security.SecureRandom专门生成令牌?

目前代码种子在启动时为java.security.SecureRandom

7 个答案:

答案 0 :(得分:224)

标准的Oracle JDK 7实现使用所谓的线性同余生成器来生成java.util.Random中的随机值。

取自java.util.Random源代码(JDK 7u2),来自方法protected int next(int bits)的注释,该方法是生成随机值的方法:

  

这是一个线性同余伪随机数发生器,如     由D. H. Lehmer定义并由Donald E. Knuth描述     计算机编程艺术,第3卷:     半数值算法,第3.2.1节。

线性同余发生器的可预测性

Hugo Krawczyk写了一篇关于如何预测这些LCG的相当好的论文(“如何预测同余发生器”)。如果您很幸运并感兴趣,您仍然可以在网上找到免费的可下载版本。还有更多的研究清楚地表明,您应该从不使用LCG用于安全关键目的。这也意味着你的随机数现在是可预测的,你不想要会话ID之类的东西。

如何打破线性同余生成器

假设攻击者必须等待LCG在完整周期后重复才是错误的。即使具有最佳循环(其递归关系中的模数m),也可以在比完整循环少得多的时间内预测未来值。毕竟,它只是需要解决的一堆模块化方程式,一旦您观察到足够的LCG输出值,就会变得容易。

通过“更好”的种子,安全性不会提高。如果您使用SecureRandom生成的随机值进行种子,或者甚至通过多次掷骰子来生成该值,则无关紧要。

攻击者只需根据观察到的输出值计算种子。在java.util.Random的情况下,这比<^>显着更少时间。不相信者可以尝试这个experiment,其中显示你可以预测未来的Random输出仅观察到两个(!)输出值,大约为2 ^ 16。在现代计算机上甚至不需要一秒钟来预测随机数的输出。

结论

替换您当前的代码。仅使用SecureRandom。那么至少你会有一点保证结果很难预测。如果您想要加密安全PRNG的属性(在您的情况下,这就是您想要的),那么您只需要使用SecureRandom。聪明地改变它应该使用的方式几乎总会导致一些不太安全的东西......

答案 1 :(得分:70)

随机只有48位,而SecureRandom最多可以有128位。因此,在securerandom中重复的可能性非常小。

随机使用system clock作为种子/或生成种子。因此,如果攻击者知道种子生成的时间,则可以轻松复制它们。但是 SecureRandom 从你的Random Data获取os(它们可以是击键之间的间隔等 - 大多数操作系统收集这些数据将它们存储在文件中 - /dev/random and /dev/urandom in case of linux/solaris)并使用它作为种子。
因此,如果小令牌大小没问题(在随机的情况下),您可以继续使用您的代码而无需任何更改,因为您使用SecureRandom生成种子。但是如果你想要更大的令牌(不能受brute force attacks影响)请使用SecureRandom - 如果需要随机2^48次尝试,那么今天的高级cpu可以打破它在实际的时间。但是对于安全随机2^128的尝试将是必需的,这将需要数年和数年来与当今先进的机器一起收支平衡。

有关详细信息,请参阅 this 链接。
修改
在阅读了@emboss提供的链接之后,很明显种子,无论它是随机的, 不应该与java.util.Random一起使用。通过观察输出来计算种子非常容易。

转到SecureRandom - 使用原生PRNG (如上面链接中所示),因为每次调用/dev/random时,它会从nextBytes()文件中获取随机值}。这样,观察输出的攻击者除非控制/dev/random文件的内容(这是非常不可能的),否则将无法做出任何事情。 sha1 prng 算法仅计算一次种子,如果您的VM使用相同的种子运行数月,则可能会被被动观察输出的攻击者破解。
注意 - 如果您调用nextBytes()的速度超过您的操作系统能够将随机字节(熵)写入/dev/random,则在使用时可能会遇到麻烦NATIVE PRNG 。在这种情况下,使用SecureRandom的SHA1 PRNG实例并且每隔几分钟(或一些间隔),使用来自SecureRandom的NATIVE PRNG实例的nextBytes()的值来植入此实例。平行运行这两个将确保您定期播种真正的随机值,同时也不会耗尽操作系统获得的熵。

答案 2 :(得分:9)

如果使用相同的种子运行两次java.util.Random.nextLong(),它将生成相同的数字。出于安全原因,您希望坚持java.security.SecureRandom,因为它的可预测性要低得多。

2个类是相似的,我认为您只需要使用重构工具将Random更改为SecureRandom,并且您的大部分现有代码都可以使用。

答案 3 :(得分:3)

如果更改现有代码是一项经济实惠的任务,我建议您使用Javadoc中建议的SecureRandom类。

即使您发现Random类实现在内部使用SecureRandom类。你不应该理所当然地认为:

  1. 其他VM实现也做同样的事情。
  2. 在JDK的未来版本中实现Random类仍然使用SecureRandom类
  3. 因此,更好的选择是遵循文档建议并直接使用SecureRandom。

答案 4 :(得分:2)

java.util.Random.nextLong()的当前参考实现对方法next(int)进行两次调用,其中直接公开32位当前种子:

protected int next(int bits) {
    long nextseed;
    // calculate next seed: ...
    // and store it in the private "seed" field.
    return (int)(nextseed >>> (48 - bits));
}

public long nextLong() {
    // it's okay that the bottom word remains signed.
    return ((long)(next(32)) << 32) + next(32);
}

nextLong()结果的高32位是当时种子的位。由于种子的宽度是48位(比如说javadoc),所以只需要迭代剩下的16位(只有65.536次尝试)来确定产生第二个32位的种子。

一旦知道种子,就可以很容易地计算出所有后续标记。

直接使用nextLong()的输出,部分是PNG的秘密,其程度可以用很少的efford计算整个秘密。危险!

*如果第二个32位是负数,则需要付出一些努力,但是可以找到它。

答案 5 :(得分:2)

种子毫无意义。一个好的随机生成器在所选择的prime数中不同。每个随机生成器都从一个数字开始,并通过“环”迭代。这意味着,您使用旧的内部值从一个数字到下一个数字。但过了一段时间,你再次到达起点并重新开始。所以你运行周期。 (随机生成器的返回值不是内部值)

如果使用素数创建一个铃声,则在完成所有可能数字的完整循环之前,将选择该环中的所有数字。如果您使用非素数,则不会选择所有数字,并且您的周期会缩短。

在您再次返回第一个元素之前,更高的素数意味着更长的周期。因此,安全随机生成器只有更长的周期,再次到达开头之前,这就是为什么它更安全。您无法使用更短的周期来预测数字生成。

换句话说:你必须全部替换。

答案 6 :(得分:0)

我将尝试使用非常基本的单词,以便您可以轻松理解Random和secureRandom之间的区别以及SecureRandom类的重要性。

您是否想知道如何生成一次性密码(一次性密码)? 为了生成OTP,我们也使用Random和SecureRandom类。现在,要使您的OTP变得更强大,SecureRandom会更好,因为它尝试了2 ^ 128次才能破解OTP,这在当前机器上几乎是不可能的,但是如果使用Random Class,那么您的OTP可能会被可能破坏您数据的人破解只需2 ^ 48尝试,即可破解。