一尘不染

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

java

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

这些令牌的用途非常敏感-用于会话ID,密码重置链接等。因此,它们确实需要在密码上是随机的,以避免有人猜测它们或对它们进行暴力破解。令牌是“长”的,因此它是64位长。

该代码当前使用java.util.Random该类来生成这些令牌。的文档中java.util.Random明确指出以下内容:

java.util.Random的实例不是加密安全的。考虑改为使用SecureRandom来获取加密安全的伪随机数生成器,以供对安全敏感的应用程序使用。

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

所以,现在我的问题是-仍在java.util.Random使用进行播种java.security.SecureRandom吗?我是否需要修改代码,使其java.security.SecureRandom专门用于生成令牌?

目前,代码种子是Random启动时的一次


阅读 1069

收藏
2020-03-23

共1个答案

一尘不染

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

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

这是线性同余伪随机数生成器,由DH Lehmer定义并由Donald E. Knuth在 计算机编程艺术,第3卷: 半数值算法,第3.2.1节中进行了描述。

线性同余生成器的可预测性
Hugo Krawczyk就如何预测这些LCG撰写了一篇相当不错的论文(“如何预测同余生成器”)。如果你很幸运和感兴趣,你仍然可以在网络上找到它的免费下载版本。而且,还有更多的研究清楚地表明,绝对不应将LCG用于对安全性至关重要的目的。这也意味着你的随机数现在是可以预测的,对于会话ID等你不需要。

如何打破线性同余生成器
假设攻击者必须在一个完整的周期后必须等待LCG重复,这是错误的。即使具有最佳周期(其递归关系中的模数m),也很容易在比整个周期少的时间内预测未来值。毕竟,这只是一堆模块化方程式,需要解决,只要你观察到足够的LCG输出值,就可以轻松实现。

使用“更好”的种子无法提高安全性。不管你是否使用SecureRandom由多个骰子生成的随机值进行播种甚至生成该值都没关系。

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

结论
替换你当前的代码。SecureRandom专门使用。然后,至少你可以保证结果很难预测。如果你需要加密安全的PRNG的属性(在你的情况下,这就是你想要的),那么你就SecureRandom只能使用。聪明地改变应该使用的方式几乎总是会导致安全性降低。

2020-03-23