【问题标题】:SecureRandom with NativePRNG vs SHA1PRNGSecureRandom 与 NativePRNG 与 SHA1PRNG
【发布时间】:2015-02-21 17:29:55
【问题描述】:

我需要生成加密性强的随机数和字节数组。为此,我使用了 Java 的 SecureRandom 类。但我不确定根据加密强度选择哪种 PRNG 算法。

以下哪个实例会产生更不可预测的数字?或者他们是平等的?

SecureRandom nativePrng = SecureRandom.getInstance("NativePRNG")
SecureRandom sha1Prng = SecureRandom.getInstance("SHA1PRNG")

此外,我们可以使用“SUN”提供程序(例如SecureRandom.getInstance("SHA1PRNG", "SUN"))生成这些实例。这有什么不同吗?

提前致谢。

【问题讨论】:

    标签: java random cryptography prng


    【解决方案1】:

    作为参考。 here:

    适用于 Solaris/Linux 的本机 PRNG 实现。它与 /dev/random 和 /dev/urandom,所以只有当这些文件才可用 存在。否则,使用 SHA1PRNG 代替此类。

    SUN 提供程序可能被用作默认值(主要取决于存在的提供程序的顺序)。

    【讨论】:

      【解决方案2】:

      TL;DR:当您不确定时使用new SecureRandom(),并让系统解决。可能使用SecureRandom.getInstanceStrong() 生成长期密钥。

      不要期望随机数生成器在运行时应用程序中生成特定的输出序列,即使您自己播种也是如此。


      对于随机数生成器,总是很难说哪个是最好的。 Linux 和大多数 Unix 都有一个经过深思熟虑的随机数生成器,所以使用 /dev/random/dev/urandom,即 "NativePRNG" 并没有什么坏处。使用/dev/random 的问题是它会阻塞直到有足够的熵可用。因此,除非您对密钥生成有一些特殊要求,否则我建议您不要这样做。


      "SHA1PRNG" 使用哈希函数和计数器以及种子。算法比较简单,但是描述的不是很好。它通常被认为是安全的。由于它仅在启动期间从其中一个系统生成器中播种,因此需要较少的内核调用,因此它可能会减少资源密集型 - 在我的系统上,它的运行速度比 "NativePRNG" 快大约 9 倍(配置为使用 @ 987654342@)。两者似乎都只对我的双核 Ubuntu 笔记本电脑的一个核心征税(一次,它经常从一个核心切换到另一个核心,这可能是内核调度的罪魁祸首)。如果您需要高性能,请选择这一款,尤其是在/dev/urandom 设备在特定系统配置上运行缓慢的情况下。

      请注意,已退休 Apache Harmony 实现中的"SHA1PRNG" 与SUN 提供程序中的"SHA1PRNG" 不同(Oracle 在标准Java SE 实现中使用)。 Jakarta 中的版本也用于旧版本的 Android。虽然我无法进行全面审查,但它看起来不是很安全。

      编辑:我对此并没有错,SHA1PRNG has been shown not to be pseudo-random for versions < 4.2.2 和更多here

      请注意,"SHA1PRNG" 不是 Java SE 的实现要求。在大多数运行时它都会存在,但直接从代码中引用它会降低您的代码的可移植性。


      现在(从 Java 9 开始)OpenJDK 和 Oracle JDK 还包含多个实现,简称为 "DRBG"。这实现了 NIST 在 SP-108 中指定的动态随机位生成器列表。这些也不是 Java 实现要求。但是,如果需要符合 FIPS 标准的随机数生成器,则可以使用它们。

      但是,他们并没有改变这里的建议;如果开发人员认为这些比默认实现更好,那么他们只会将其设为默认实现。 SecureRandom 的合约不变:只需要生成随机数。过去已经对默认算法进行了更改。


      一般来说,要求特定的提供者也不是一个好主意。指定提供者可能会损害互操作性;例如,并非每个 Java 运行时都可以访问 SUN 提供程序——Android 肯定没有。它还使您的应用程序在运行时的灵活性降低,即您不能将提供程序放在列表中更高的位置并使用它。

      因此,仅当您依赖某个提供商提供的功能时,才需要指明该提供商。例如,如果您有生成随机数的特定硬件设备或已通过 FIPS 认证的加密库,您可能需要指定提供程序。如果您必须指定提供程序,最好将算法/提供程序作为您的应用程序的配置选项。

      Android developer Security Blog 中也有不指定提供者的想法。


      因此,请尽量避免选择任何特定的随机生成器。相反,只需使用空参数构造函数:new SecureRandom() 并让系统选择最佳随机数生成器。如果您有任何特定要求,例如,可以在 Java 8 及更高版本中使用新的可配置 SecureRandom.getInstanceStrong()。长期密钥生成。

      不要缓存 SecureRandom 的实例,只需让它们最初自己播种并让 VM 处理它们。我没有看到操作上有明显的不同。


      什么时候不使用SecureRandom

      作为一般警告,我强烈建议不要将随机数生成器用于随机数生成以外的任何事情。即使您可以自己播种,即使您选择 Sun 的 SHA1PRNG,也不要指望能够从随机数生成器中提取相同的随机数序列。所以不要将它用于从密码中派生密钥,仅举一个例子。

      如果您确实需要重复序列,则使用流密码并将种子信息用于密钥和 IV。加密由零组成的明文以检索伪随机值的密钥流。或者,您可以使用可扩展输出函数 (XOF),例如 SHAKE128 或 SHAKE256(如果可用)。

      如果可用的 RNG 提供的性能不足并且安全不是问题,您可能需要考虑使用不同的非安全随机数生成器来代替 SecureRandom。没有SecureRandom 实现将与非安全随机数生成器一样快,例如 Mersenne Twister 算法或Random 类实现的算法。这些已针对简单性和速度而非安全性进行了优化。

      可以扩展SecureRandom并将确定性、种子随机实现插入到库调用中。这样,库检索具有明确定义输出的伪随机数生成器。然而应该注意,随机数生成器可以由算法以不同的方式使用。例如。 RSA 可能会切换到更好的优化方式来查找素数,并且 DES 密钥可以通过调整或直接计算的奇偶校验位生成。

      【讨论】:

      • 如果你只是盲目地信任系统获取的任何 SecureRandom 服务,它会不会打开一个安全漏洞?
      • 不,不是。如果系统不能被信任,随机数生成器只是问题的一小部分。除此之外,其他随机数生成器通常由操作系统的随机数生成器播种。所以他们仍然依赖于系统RNG的安全性。解决这个问题的唯一方法是拥有自己的熵源。较新的 Intel 芯片具有 RDRAND 指令,但如果是 Java,您首先需要转为原生才能使用它。
      • @cygnusv 确实如此。但是,如果您不定义 PRNG,就会得到这样的结果。 SUN 的实现随着时间的推移发生了变化,Apache Harmony(旧的 Android 版本)不兼容且完全不安全。
      • @MaartenBodewes 答案很好,但我更喜欢检查自己的来源,这里是 JDK8 源代码:sun.security.provider.SecureRandomsun.security.provider.NativePRNGsun.security.provider.SeedGenerator
      • @FrederickNord 1) 算法是未知的,并且可能(并且确实)在实现之间发生变化,并且种子可能不会用作状态的唯一输入,算法可能由操作系统预先播种. 2) 使用 PBKDF2,功能内置于 Java/JCE。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-02-15
      • 2017-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      相关资源
      最近更新 更多