正如 CodesInChaos 已经表明的那样,SUN 提供程序的默认实现使用系统随机数生成器自动为自己播种。由于 Java 本身没有(显式)熵源,因此它的种子或多或少依赖于系统。
在从 SUN 提供程序中的 "SHA1PRNG" 检索数据之前,您永远不应调用 setSeed,因为这会使您的 RNG(随机数生成器)成为确定性 RNG - 它仅使用给定种子而不是将种子添加到状态。换句话说,它总是会生成相同的伪随机位或值流。
对setSeed 的初始调用可能因提供商而异。有时它会将种子用作唯一的种子,但它也可能只是将种子添加到当前状态。在更高的 Android 版本(4.2 及更高版本)上,种子只是添加到随机状态,因此 "SHA1RNG" 将保持完全随机。
生成随机数生成器的最佳方式可能就是
SecureRandom r = new SecureRandom();
让 Java 运行时找出最好的那个。
如果您想使用显式算法(但是,SUN/Oracle 对该算法的描述不正确),那么您可以使用:
SecureRandom r = SecureRandom.getInstance("SHA1PRNG");
就像在你的代码中一样。
现在,还可以使用 NIST 算法,使用 "DRBG" 作为算法描述,然后您可以使用安全属性为 VM 配置。
"SHA1PRNG" 和 "DRBG" 都不是实现要求,并且每个运行时/提供者使用哪种算法或如何播种可能会有所不同。我永远不会使用它们来重新生成先前生成的字节或值流;请为此使用流密码。例如,您可以将密码实例 Cipher 用于 "AES/CTR/NoPadding" 并加密零值字节以获取密钥流。
如果要添加熵,请使用:
// just used to make sure that the SecureRandom is seeded by the OS
r.nextBytes(new byte[8]);
r.setSeed(1232);
一个常数值或文字不包含太多(如果有的话)熵。通常的熵来源是当前时间(甚至更好,System.nanoTime())、鼠标移动等。
对于 Java 8,有一个新方法 getInstanceStrong(),其描述如下:
返回使用在 securerandom.strongAlgorithms 安全属性中指定的算法/提供程序选择的 SecureRandom 对象。
某些情况需要强随机值,例如在创建 RSA 公钥/私钥等高价值/长期存在的机密时。为了帮助指导应用程序选择合适的强 SecureRandom 实现,Java 发行版在 securerandom.strongAlgorithms 安全属性中包含了一个已知强 SecureRandom 实现的列表。
应该用它来代替对构造函数的调用。请注意,这可能会返回一个阻塞 RNG,即:一个可能会阻塞您的线程直到足够的熵可用的RNG。它还可能会耗尽您的操作系统熵池阻塞其他应用程序,因此请谨慎使用它。