SecureRandom:init需要一次还是每次需要?

我们的团队使用SecureRandom生成密钥对列表(SecureRandom传递给KeyPairGenerator)。 我们无法就以下两个选项中的哪一个达成一致:

  1. 每次我们需要生成密钥对时创建一个新实例

  2. 初始化静态实例并将其用于所有密钥对

哪种方法通常更好, 为什么

补充:我的直觉是第二种选择更安全。 但我唯一的论点是基于假设伪随机性是从当前时间戳得出的理论攻击:有人可能会看到密钥对的创建时间,猜测周围时间间隔内的时间戳,计算可能的伪随机序列,并获得关键材料。

补充:我对基于时间戳的确定性的假设是错误的。 这是Random和SecureRandom之间的区别。 因此,看起来答案是:就安全性而言,它并不重要。

java.util.Random类不同, java.security.SecureRandom类必须在每次调用时产生非确定性输出。

这意味着,对于java.util.Random ,如果您每次需要一个新的随机数时重新创建一个具有相同种子的实例,那么每次都会得到相同的结果。 但是, SecureRandom保证不会这样做 – 因此,每次创建单个实例或创建新实例不会影响它生成的随机字节的随机性。

那么,从正常的良好编码实践角度来看,为什么要创建太多实例呢?

对于SecureRandom,您可能希望通过如下调用偶尔重新调整( 在大多数情况下使用系统熵 ):

 mySecureRandom.setSeed(mySecureRandom.generateSeed(someInt)); 

以便为潜在的攻击者提供一些不到无限的时间来发现你的密钥。

在Justice League博客上有一些关于这种考虑的好文章。

初始化静态实例并将其用于所有密钥对。 它不会或多或少随机。

每个SecureRandom生成都是从一些熵池中播种的。 根据所使用的操作系统,这可能是操作系统维护的熵池,如Linux上的/dev/random ,也可能是JVM所做的事情。 在一些早期的实现中,Sun JVM用于生成许multithreading并使用它们的时序数据来创建种子。

在每次调用时创建新的SecureRandom可能会导致应用程序运行速度变慢,因为种子的创建可能会阻塞。 最好重用一个静态创建的实例,但确保在从中提取固定数量的随机字节后重新设置它。

您可能希望在SecureRandom实例上创建一个包装器,该实例计算在nextBytesgenerateSeed调用中提取的字节数,并在多个字节之后,使用系统熵池重新安装内部SecureRandom实例。

然而,在Linux上的Java上,封装器方法是不可能的,因为从新的SecureRandom()获得的SecureRandom实例只不过是/dev/random的包装器,并且每次调用nextBytesgenerateSeed实际上都会耗尽OS熵池。 在Linux和Solaris上,最好使用JCE提供程序来创建SecureRandom

我不会依赖SecureRandom而不是加密安全的PRNG。 Gowri在javadocs中使用的完整引用是:

此外,SecureRandom必须产生非确定性输出,因此要求种子材料不可预测,并且SecureRandom的输出必须是加密强序列,如RFC 1750:安全性随机性建议中所述。

从中可以看出真正的期望是什么 – RFC 1750详细说明了使用硬件来增强随机数生成,但javadocs说“因此需要种子材料不可预测”,这似乎与此相矛盾。

最安全的假设是,您的SecureRandom实现只是一个加密安全的PRNG,因此您的密钥不比您使用的随机种子更安全。 因此,使用每个密钥的新(唯一,真正随机)种子初始化新的SecureRandom将是最安全的选择。

一旦应该就够了。 我的经验也是初始化SecureRandom类型的生成器有时也会很慢(由于实现了随机性),所以你应该考虑到这一点。

你为什么每次都要创建一个新实例? 这不是随意的。 我认为最好初始化一次并将其用于所有对。