函数nextXXX()(例如nextInt(),nextFloat()和nextBytes())与Java的SecureRandom类中的generateSeed(int numBytes): byte[]有什么区别? "generateSeed"中的“种子生成算法”与安全随机生成器本身有何不同?
generateSeed()
并不使用由随机数生成器生成的任何字节。相反,它只是一个 穿透到 SecureRandom
实现使用的熵源,在其自身需要种子时进行种子化。
例如,在 Oracle 提供的 Java SE 上调用以下代码:
// initSeed is just zero valued bytes
byte[] initSeed = new byte[16];
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(initSeed);
byte[] seed = secureRandom.generateSeed(16);
byte[] data = new byte[16];
secureRandom.nextBytes(data);
System.out.printf("Seed: %s%n", Hex.toHexString(seed));
System.out.printf("Data: %s%n", Hex.toHexString(data));
对于seed
,实际上会给出不同的值,而对于data
,则总是给出相同的值。换句话说,generateSeed
使用操作系统请求16字节的熵来生成种子,而随机数生成器只被initSeed
初始化,因此总是生成相同的伪随机数流。
警告:这仅用于说明问题;您不应该依赖任何SecureRandom
实例返回除随机字节之外的任何内容。关于setSeed
的行为因不同的实现而异。Oracle的"SHA1PRNG"
提供程序将其作为唯一的种子,其他提供程序可能会选择将其混合到PRNG状态中(例如,较新的Android实现始终会生成随机数据)。
随机数函数依赖于一个初始值,从该值生成一系列随机数(请了解PRNG - 伪随机数生成)。next
函数将返回从该初始值(种子)生成的下一个数字。generateSeed()
将为PRNG生成一个新的初始值。
nextXYZ()
和generateSeed()
做的事情完全相同,通常如此。
SecureRandom.nextBytes()
和SecureRandom.generateSeed()
实际上只是简单的包装器,将调用转发到SecureRandomSpi.engineGenerateSeed()
和SecureRandomSpi.engineNextBytes()
,它们分别包含实际的实现。
SecureRandomSpi
的具体实现取决于所选的算法,当然!然而,对于Windows平台上的默认SecureRandom算法(即"Windows-PRNG"
),我们可以从源代码中看到engineNextBytes()
和engineGenerateSeed()
做的事情完全相同:它们都调用一个名为PRNG.generateSeed()
的本地方法,这是一个最终调用Win32 API函数CryptGenRandom()
的C++函数,该函数来自操作系统。[Secure]Random.nextXYZ()
方法,比如nextInt()
,nextLong()
或nextDouble()
,都会调用一个名为next()
的内部方法,该方法提供了所需的随机位数。而且,不足为奇的是,next()
方法最终会调用nextBytes()
方法 - 因此,在SecureRandom
的情况下,会调用SecureRandomSpi.engineNextBytes()
。