在我们的应用程序中,我们使用Random和SecureRandom为某些客户端生成一些非常大的信息集。经过一些测量后,我们意识到使用存储在磁盘上的种子重新生成信息比存储和读取信息更快,内存更少。我查看了javadoc,并且我没有看到任何保证,给定常量种子n,例如new Random(n).nextInt()
的结果在不同版本的Java的。
我的问题是:对于Java 8和以前的版本来说这是一个安全的假设吗,Java 8中的new Random(n).nextInt()
应该返回与之前版本的Java相同的值吗? / p>
(为了公平起见,我会接受回答上述问题的答案,即使他们不回答下面的问题。)
虽然我知道不能保证对于以后的Java版本也是如此,并且第二个问题应该征求一些意见,你认为未来的Java版本将改变用于生成的算法的几率是多少使用Random和SecureRandom的伪随机数?
谢谢!
答案 0 :(得分:9)
是的,在通过Java 8的所有版本中Random
都可以保证这一点。但是SecureRandom
似乎没有任何类似的保证。
如果查看Javadocs for Random,您可以看到:
如果使用相同的种子创建了两个Random实例,并且为每个实例创建了相同的方法调用序列,则它们将生成并返回相同的数字序列。 为了保证此属性,为Random类指定了特定的算法。为了Java代码的绝对可移植性,Java实现必须使用此处显示的所有算法用于Random类。但是,Random类的子类允许使用其他算法,只要它们遵守一般合同即可对于所有方法。
强调我的。因此,一些想法明确地给出了这个问题,并且决定在类的文档中指定底层算法。从理论上讲,人们可以提供不同的实现,但是它不符合Java规范。
有关进一步的证据,请查看版本之间的实现。例如,next
方法在每个版本中的定义完全相同,至少从Java 1.4开始(早在我看来)。
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
return (int)(seed >>> (48 - bits));
由于这是相同的,因此您可以清楚地保证,不仅版本之间返回的值相同,而且您可以使用此算法自行计算相同的值。
但是,我无法找到SecureRandom
的任何类似保证,并且由于Random
表示子类可以自由地违反此规则,因此无法保证它会版本之间保持一致。我能找到的唯一证据就是观察用它生成不一致值的人a forum post。根据{{3}},SecureRandom
似乎确实在实践中产生了一致的价值观,因此您可能会认为值得冒这个风险来假设它会起作用。但是,似乎无法保证这种行为。
要回答问题的第二部分,从理论上讲,他们可以改变它。但是,由于它目前是一个定义明确的操作,并且考虑到Oracle在避免向后兼容性方面的工作有多么困难,我认为这非常不可能。
答案 1 :(得分:3)
直接回答您的确切问题:&#34;这是Java 8及更早版本的安全假设吗?&#34;我做到了这一点(Handy我保留了一些旧的JDK用于这种情况)。
import java.util.Random;
public class RandomTest {
public static void main(String[] args) {
System.out.println("Random = " + new Random(0).nextInt());
System.out.println("SecureRandom = " + new SecureRandom(new byte[] {0, 0, 0, 0}).nextInt());
}
}
结果:
> & 'C:\Program Files\Java\jdk1.6.0_45\bin\javac.exe' .\RandomTest.java
> & 'C:\Program Files\Java\jdk1.6.0_45\bin\java.exe' RandomTest
Random = -1155484576
SecureRandom = -1439655693
> & 'C:\Program Files\Java\jdk1.7.0_17\bin\javac.exe' .\RandomTest.java
> & 'C:\Program Files\Java\jdk1.7.0_17\bin\java.exe' RandomTest
Random = -1155484576
SecureRandom = -1439655693
> & 'C:\Program Files\Java\jdk1.8.0_25\bin\javac.exe' .\RandomTest.java
> & 'C:\Program Files\Java\jdk1.8.0_25\bin\java.exe' RandomTest
Random = -1155484576
SecureRandom = -1439655693
是的。根据经验,它似乎是至少 Java 6,7和8的安全假设。
修改强>
上面更新了SecureRandom
。 SecureRandom
没有一个带int
的构造函数,所以我用四个字节的零(对于相同的32位)播种。对于所有经过测试的Java版本,行为都是一致的,但与java.util.Random
的结果不同。