【发布时间】:2012-08-28 06:31:46
【问题描述】:
这段代码安全吗?
SecureRandom randomizer = new SecureRandom(String.valueOf(new Date().getTime()).getBytes());
这是实例化安全随机种子的正确方法吗?
【问题讨论】:
标签: java security encryption random cryptography
这段代码安全吗?
SecureRandom randomizer = new SecureRandom(String.valueOf(new Date().getTime()).getBytes());
这是实例化安全随机种子的正确方法吗?
【问题讨论】:
标签: java security encryption random cryptography
我认为最好让 SecureRandom 自己播种。这是通过在创建后立即调用 nextBytes 来完成的(调用 setSeed 会阻止这种情况)。
final byte[] dummy = new byte[512];
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.nextBytes(dummy);
您想使用 SHA1PRNG,因为它保证了快速的非阻塞实现,即使在 Linux 上也是如此,默认情况下不是这样。
【讨论】:
nextBytes() 调用只是为了播种,我不会使用 512 byes,但出于效率原因只使用 1。
不,您应该避免使用 SecureRandom(byte[]) 构造函数。它既不安全也不便携。
它是不可移植的,因为它在 Windows 和其他操作系统上的行为不同。
在大多数操作系统上,默认算法是“NativePRNG”,它从操作系统(通常是"/dev/random")获取随机数据并忽略您提供的种子。
在 Windows 上,默认算法是“SHA1PRNG”,它将您的种子与计数器结合起来并计算结果的哈希值。
在您的示例中这是个坏消息,因为输入(当前 UTC 时间,以毫秒为单位)的可能值范围相对较小。例如,如果攻击者知道 RNG 是在过去 48 小时内播种的,他们可以将种子缩小到小于 228 个可能的值,即您只有 27 位熵。
另一方面,如果您在 Windows 上使用了默认的 SecureRandom() 构造函数,它将调用本机 CryptoGenRandom 函数来获取 128 位种子。因此,通过指定您自己的种子,您已经削弱了安全性。
如果您真的想覆盖默认种子(例如用于单元测试),您还应该指定算法。例如
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed("abcdefghijklmnop".getBytes("us-ascii"));
另见How to solve performance problem with Java SecureRandom?
还有这篇博文:http://www.cigital.com/justice-league-blog/2009/08/14/proper-use-of-javas-securerandom/
【讨论】:
"SHA1PRNG",也可能会有所不同,因此在这方面它有点危险。
/dev/random 使用用户提供的种子。本机 Windows RNG 可以,但是当我发布这个答案时,JRE 没有利用这一点。那是 4 年前的事了,所以从那以后它可能已经改变了。
该代码相当安全,因为它不只是使用给定的种子来为随机发生器播种。
它并不比仅仅使用更随机。
SecureRandom randomizer = new SecureRandom();