且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2023-01-11 21:32:02

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

The standard Oracle JDK 7 implementation uses what's called a Linear Congruential Generator to produce random values in java.util.Random.

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

Taken from java.util.Random source code (JDK 7u2), from a comment on the method protected int next(int bits), which is the one that generates the random values:

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

This is a linear congruential pseudorandom number generator, as defined by D. H. Lehmer and described by Donald E. Knuth in The Art of Computer Programming, Volume 3: Seminumerical Algorithms, section 3.2.1.

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

Hugo Krawczyk 写了一篇关于如何预测这些 LCG 的非常好的论文(如何预测同余生成器").如果您幸运且感兴趣,您仍然可以在网络上找到它的免费、可下载版本.还有大量研究清楚地表明,您应该永远不要将 LCG 用于安全关键目的.这也意味着您的随机数现在可预测的,这是您不希望会话 ID 等的东西.

Predictability of Linear Congruential Generators

Hugo Krawczyk wrote a pretty good paper about how these LCGs can be predicted ("How to predict congruential generators"). If you're lucky and interested, you may still find a free, downloadable version of it on the web. And there's plenty more research that clearly shows that you should never use an LCG for security-critical purposes. This also means that your random numbers are predictable right now, something you don't want for session IDs and the like.

攻击者必须等待 LCG 在一个完整周期后重复的假设是错误的.即使使用***循环(其递推关系中的模数 m),也很容易在比完整循环更短的时间内预测未来值.毕竟,这只是一堆需要求解的模方程,只要你观察到足够多的 LCG 输出值,就变得容易了.

The assumption that an attacker would have to wait for the LCG to repeat after a full cycle is wrong. Even with an optimal cycle (the modulus m in its recurrence relation) it is very easy to predict future values in much less time than a full cycle. After all, it's just a bunch of modular equations that need to be solved, which becomes easy as soon as you have observed enough output values of the LCG.

更好"的种子不会提高安全性.如果您使用 SecureRandom 生成的随机值作为种子,或者甚至通过掷骰子数次来产生该值,这根本无关紧要.

The security doesn't improve with a "better" seed. It simply doesn't matter if you seed with a random value generated by SecureRandom or even produce the value by rolling a die several times.

攻击者将简单地根据观察到的输出值计算种子.在 java.util.Random 的情况下,这比 2^48 花费的时间明显少.不信的人可以试试这个实验,它表明你可以预测未来的 Random 输出,在大约 2^16 的时间内仅观察两个(!)输出值.现在,在现代计算机上预测随机数的输出甚至不需要一秒钟.

An attacker will simply compute the seed from the output values observed. This takes significantly less time than 2^48 in the case of java.util.Random. Disbelievers may try out this experiment, where it is shown that you can predict future Random outputs observing only two(!) output values in time roughly 2^16. It takes not even a second on a modern computer to predict the output of your random numbers right now.

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

Replace your current code. Use SecureRandom exclusively. Then at least you will have a little guarantee that the result will be hard to predict. If you want the properties of a cryptographically secure PRNG (in your case, that's what you want), then you have to go with SecureRandom only. Being clever about changing the way it was supposed to be used will almost always result in something less secure...