我需要生成加密强度高的随机数和字节数组。为此,我正在使用 Java 的 SecureRandom
类。但我不确定根据密码强度选择哪种 PRNG 算法。
以下哪个实例会产生更不可预测的数字?或者他们是平等的?
SecureRandom nativePrng = SecureRandom.getInstance("NativePRNG")
SecureRandom sha1Prng = SecureRandom.getInstance("SHA1PRNG")
此外,我们能够使用“SUN”提供程序生成这些实例(例如 SecureRandom.getInstance("SHA1PRNG", "SUN")
)。这有什么不同吗?
提前致谢。
原文由 ovunccetin 发布,翻译遵循 CC BY-SA 4.0 许可协议
TL;DR:当您不确定时使用
new SecureRandom()
并让系统找出答案。可能使用SecureRandom.getInstanceStrong()
进行长期密钥生成。不要指望随机数生成器在运行时应用程序中生成特定的输出序列,即使您自己播种也不行。
对于随机数生成器,总是很难说哪个是最好的。 Linux 和大多数 Unix 都有一个经过深思熟虑的随机数生成器,所以使用
/dev/random
或/dev/urandom
并没有什么坏处,即"NativePRNG"
使用/dev/random
的问题是它会阻塞,直到有足够的熵可用。所以我建议不要这样做,除非您对密钥生成有一些特殊要求。"SHA1PRNG"
使用散列函数和计数器,以及种子。算法比较简单,但是一直没有很好的描述。它通常被认为是安全的。因为它只在启动期间从其中一个系统生成器播种,因此需要更少的内核调用,所以它可能占用更少的资源——在我的系统上,它的运行速度比"NativePRNG"
(这是配置为使用/dev/urandom
)。两者似乎都只对我的双核 Ubuntu 笔记本电脑的一个核心征税(一次,它经常从一个核心切换到另一个核心,这可能是内核调度的罪魁祸首)。如果您需要高性能,请选择这个,尤其是当/dev/urandom
设备在特定系统配置上速度慢时。请注意, 退役 的 Apache Harmony 实现中的
"SHA1PRNG"
与 SUN 提供程序中的不同(甲骨文在标准 Java SE 实现中使用)。 Jakarta 中的版本也用于旧版本的 Android。虽然我无法进行全面审查,但它看起来并不是很安全。编辑:我对此没有半点错误, SHA1PRNG 已被证明对于版本 < 4.2.2 和更多版本不是伪随机的 。
请注意
"SHA1PRNG"
不是 Java SE 的实现要求。在大多数运行时它都会存在,但直接从代码中引用它会使代码的可移植性降低。如今(从 Java 9 开始)OpenJDK 和 Oracle JDK 还包含多个简单称为
"DRBG"
的实现。这实现了 NIST 在 SP-108 中指定的动态随机位生成器列表。这些也不是 Java 实现要求。但是,如果需要符合 FIPS 的随机数生成器,则可以使用它们。但是,他们不会更改此处的建议;如果开发人员认为这些比默认实现更好,那么他们只会将其设为默认实现。
SecureRandom
的合同没有改变:它只是需要生成随机数。过去已经对默认算法进行了更改。一般来说,要求特定的提供者也不是一个好主意。指定提供者可能会损害互操作性;例如,并非每个 Java 运行时都可以访问 SUN 提供程序——Android 肯定没有。它还会降低您的应用程序在运行时的灵活性,即您不能将提供者放在列表中较高的位置并使用它来代替。
因此,仅当您依赖提供者提供的其中一项功能时才指明提供者。例如,如果您有生成随机数的特定硬件设备或已通过 FIPS 认证的加密库,您可能希望指定提供程序。如果您必须指定提供程序,那么让算法/提供程序成为您的应用程序的配置选项可能是个好主意。
不指定提供商的想法也出现在这个 Android 开发人员安全博客 中。
因此,请尽量避免选择任何特定的随机生成器。相反,只需使用空参数构造函数:
new SecureRandom()
并让系统选择最佳随机数生成器。如果您对长期密钥生成有任何特定要求,则可以在 Java 8 及更高版本中使用新的可配置SecureRandom.getInstanceStrong()
。不要缓存
SecureRandom
的实例,只是让它们最初播种并让 VM 处理它们。我没有看到操作上的明显差异。什么时候不使用
SecureRandom
:作为一般性警告,我强烈建议不要将随机数生成器用于随机数生成以外的任何用途。即使您可以自己播种,即使您选择 Sun 的 SHA1PRNG, _也不要指望能够从随机数生成器中提取相同的随机数序列_。因此, 不要 将它用于从密码派生密钥,仅举一个例子。
如果您确实需要重复序列,则使用流密码并将种子信息用于密钥和 IV。加密由零组成的明文以检索伪随机值的密钥流。或者,您可以使用可扩展输出函数 (XOF),例如 SHAKE128 或 SHAKE256(如果可用)。
如果可用的 RNG 提供的性能不足并且安全性不是问题,您可能需要考虑使用不同的、非安全的随机数生成器而不是
SecureRandom
。没有SecureRandom
实现将与非安全随机数生成器一样快,例如 Mersenne Twister 算法或Random
类实现的算法。这些已针对简单性和速度而不是安全性进行了优化。可以 扩展
SecureRandom
类 并将确定性的、种子随机实现插入到库调用中。通过这种方式,库可以检索具有明确输出的伪随机数生成器。然而,应该注意的是,算法可以以不同的方式使用随机数生成器。例如,RSA 可能会切换到一种更好的优化方式来查找素数,并且 DES 密钥可能会使用调整后的或直接计算的奇偶校验位来生成。