【问题标题】:Is SecureRandom.ints() secure?SecureRandom.ints() 安全吗?
【发布时间】:2019-12-07 12:44:20
【问题描述】:

众所周知,SecureRandom 类为生成的随机数提供了强大的加密安全性。 java.util.Random 对于需要加密安全的情况是不安全的。 SecureRandom 的典型用法是:

SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);

不过,我遇到了一个案例:

SecureRandom random = new SecureRandom();
int number = random.ints();

方法ints() 继承自java.util.Random 类。当SecureRandom这是一个安全的随机数生成器使用从不安全的随机数生成器继承的方法时,我感到困惑,它是否安全?

【问题讨论】:

  • 补充我的赞成票:这是一个非常的好问题,标题清晰,描述简洁,结构良好。如果我们每天看到的更多问题可以是这样的!谢谢!
  • 你的意思是IntStream numbers = random.ints();

标签: java security random


【解决方案1】:

是的,SecureRandom

提供强大的加密随机数生成器 (RNG)。

安全 RNG 的一个重要因素是种子。

因此,传递给 SecureRandom 对象的任何种子材料都必须是不可预测的,并且所有 SecureRandom 输出序列都必须具有强大的加密能力,如 RFC 4086:安全的随机性要求中所述。

继续使用它。如果您对细节感兴趣,请阅读描述实现使用的各种方法的 JavaDoc。

【讨论】:

  • 您断言“安全 RNG 的重要因素是种子,而不是算法。”是错误的并且具有危险的误导性。种子和算法都至关重要。如果您将种子输入一个粗糙的 LCG 或 Mersenne Twister 或其他一些非加密 RNG 算法,那么您的种子的熵源有多好并不重要。
  • 众所周知,Android 使用了错误的 SHA1PRNG 实现,是的,您可以向 RNG 提供种子以使其不安全(在某些情况下)。如果您只使用new SecureRandom,那么种子通常取决于系统——以防SecureRandom 来自大多数Java 运行时的本地提供程序。
  • @monica i 认为之所以造成混淆,是因为生成器中真正随机性最重要的一个因素是种子及其熵,但是正如您所提到的,由于软件的技术限制(例如数量大小),仅真正的随机性并不能使数字“安全”。但这更像是一个挑战而不是一个陈述,因此我从 i think 开始。这对于理解量子计算何时成为标准肯定是至关重要的。 ?
【解决方案2】:

Random.ints() 是一个返回IntStream 的方法。 IntStream 既不安全也不不安全:它是一个数字流。

方法返回的整数序列的“安全性”取决于方法的实现。 SecureRandomRandom 更安全地生成其“随机”值。它们共享相同的 API,因此您可以根据自己的要求在给定的上下文中使用其中任何一种。

因此,它从不安全的类继承的事实与安全性无关:您可以合理地相信 SecureRandom 类与文档所说的一样安全。


考虑与HashSet的类比:this makes no guarantees of the iterator ordering;然而,LinkedHashSetHashSet 的子类确实保证了迭代器的排序。 LinkedHashSet 的保证与HashSet 的保证是一致的,因为 特定的排序 是“无保证排序”可以观察到的可能排序之一(毕竟,你必须按一些顺序返回元素)。

同样,Random 不保证返回的整数序列的安全性; SecureRandom 提供更强有力的保证。但是,巧合的是,SecureRandom 中的整数序列不能也被 Random 返回是没有理由的。

【讨论】:

  • 我认为这也是一个很好的例子,说明您可能不想“对接口进行编程”。代码可以写成Random random = new SecureRandom(); 并且仍然可以编译和工作。但在这里,您确实希望拥有的不仅是具有所需方法签名的任何实现,而且是保证以某种方式运行的特定实现。
  • @Thilo 我同意。这类似于 Google 的内部风格建议,我们尽可能使用 Guava ImmutableList,而不是 List:更具体的类型传达了有用的信息。
  • ...您与LinkedHashSetSet 的类比使同一点变得更好。
  • @LutzHorn。我不同意。您不仅需要正确类型的 ints() 方法(或 iterator() 方法)。您还希望有具体的实施保证。
  • @LutzHorn 不,这并不总是正确的。请参阅我关于 ImmutableListList 的评论:您可能只想在该列表中调用 get(i),但您不得调用 set 的事实已被强烈表明(并且,例如,can be caught by the compiler)。
【解决方案3】:

是的,它是安全的。

java.util.Random 的代码检查表明ints() 创建了一个拆分器,该拆分器使用internalNextInt(...) 生成随机整数。这反过来又在this 上调用nextInt()。在java.security.SecureRandom 的情况下,nextInt() 被覆盖以生成“安全”随机数1

您可以通过查看源代码自行确认。


1 - 当然,将整数或整数序列称为“安全”实际上是没有意义的。在某些情况下,SecureRandom 可能没有您需要的属性。 (这取决于类使用的实际 RNG 或 PRNG 实现、提供的种子或系统提供的熵源的质量等。)但是 SecureRandom::ints() 将生成具有相同属性的整数序列就好像您对同一个对象进行了一系列 SecureRandom::nextInt() 调用。如果后一个序列适合您的目的(无论它们是什么),那么前一个也是。

【讨论】:

    【解决方案4】:

    整数方法

    是的,它是安全的,只要nextInt() 是安全的(对于从流中检索的整数数量)。

    根据documentation of the Random#ints() method

    生成一个伪随机的int 值,就好像它是调用方法nextInt() 的结果一样。

    现在反过来,Random#nextInt

    方法nextInt由类Random实现,如同(返回)next(32)

    next(int) 是一个您不能调用的受保护方法,但可以在实现类时被覆盖。

    这又被实现为SecureRandom.next(32)如果您使用SecureRandom 的实例而不是Random

    生成一个包含用户指定数量的伪随机位的整数(右对齐,前导零)。此方法覆盖java.util.Random 方法,并用于为从该类继承的所有方法提供随机位源(例如,nextInt、@ 987654339@ 和 nextFloat)。

    所以最后调用了SecureRandom 的方法,如果这是安全的,那么随机数流就是安全的。现在说实话,该语句是错误的,因为它没有用于nextBytes,但它肯定用于任何返回数字值的方法。

    SecureRandom 实现

    如果您已经确定您使用的SecureRandom 是安全的,那么您可以在此处停止阅读。

    现在你会认为它会到此结束,但 SecureRandom 本身实现随机 bit 生成器。相反,它只依赖于配置的提供者中实现SecureRandomSpi 的特定服务类(Spi 表示服务提供者接口,提供者必须实现的抽象类以将安全随机数据传递给SecureRandom 的实例)。所以最后必须在SecureRandom#next(int) 内调用SecureRandomSpi#nextBytes() 以检索实际位。


    正如Random#next(int) 所述,其他需要数字或数字流的方法最终都应该调用这个特定的方法。现在是否确实如此取决于RandomSecureRandom 的实现。结果是否安全取决于算法和SecureRandomSpi 提供的种子,实际使用的随机位生成器。


    我已经在上面证明了结果应该确实是密码随机的,假设SecureRandom 是密码随机的。最后,对 Java 的每个实现和版本只需要进行一次完整的代码审查,以证明它是安全的。不过,这超出了 StackOverflow 的范围,您可能需要来自例如的支持。 Oracle、IBM 或 Google 来检索测试文档和审查,当然,您也可以自己为特定实施执行此类审查。毕竟大部分——如果不是全部的话——都是开源的。

    【讨论】:

      【解决方案5】:

      是的,

      以前的解决方案是使用伪随机数。 SecureRandom 是一个加密性强的随机数生成器。

      实施与之前的实施仅略有不同。坦率地说,它是 Random 的直接替代品:

      public static void main(String[] args) {
          SecureRandom current = new SecureRandom();
          // [0, 101)
          System.out.println(current.nextInt(101));
          System.out.println(current.nextInt(101));
      }
      

      更多信息请见Java Random Number Generation

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-02-03
        • 1970-01-01
        • 2010-11-20
        • 2016-03-06
        • 2010-09-26
        • 2018-04-09
        • 2020-04-22
        • 2013-10-09
        相关资源
        最近更新 更多