Java 8 SecureRandom 生成随机数

Java 8的SecureRandom API对原有的有几个变化

根据Oracle,已经做出了以下有趣的变化:

  • 对于类UNIX平台,已经引入了两个新的实现,它们提供了阻塞和非阻塞行为:NativePRNGBlockingNativePRNGNonBlocking
  • 所述getInstanceStrong()方法是在JDK 8中引入此方法返回的每个平台上可用的最强SecureRandom实现的一个实例。

我们来看看这些变化,看看它们对应用程序的意义。请注意,这些详细信息仅适用于与SUN和SunMSCAPI Java加密扩展提供程序一起分发的Oracle JDK / JRE。

新的实现:NativePRNGBlocking / NativePRNGNonBlocking

NativePRNG在Solaris / Linux / MacOS上的行为是sun.security.provider.NativePRNG.generateSeed()可能会阻塞,因为它使用/ dev / random,但是使用nextBytes()nextLong()获取输出将永远不会因为他们使用/ dev / urandom(这对于默认环境设置是正确的)。NativePRNG仍然是这样的,但现在有两个额外的实现:

  • NativePRNGBlocking对所有以下操作使用/ dev / random:
    • 初始播种:这将使用/ dev / random中的20个字节初始化内部SHA1PRNG 实例
    • 调用nextBytes()nextInt()等:这提供了内部SHA1PRNG  实例(见上文)的输出和从/ dev / random读取的数据的XOR
    • 调用getSeed():这提供从/ dev / random读取的数据
  • 类似地,NativePRNGNonBlocking对所有以下操作使用/ dev / urandom:
    • 初始种子:这将使用/ dev / urandom中的20个字节初始化内部SHA1PRNG实例
    • 调用nextBytes()nextInt()等:这提供了内部SHA1PRNG 实例(见上文)的输出的XOR 和从/ dev / urandom读取的数据
    • 调用getSeed():这提供从/ dev / urandom读取的数据

在系统收集更多熵时,您不希望应用程序阻止的任何应用程序中,NativePRNGBlocking将会遇到问题。从它获取任何类型的输出可能会导致应用程序的线程挂起。在桌面应用程序中用于生成本地加密密钥(例如)是很好的,但在Web应用程序中几乎不可行。

请注意,NativePRNGBlocking在其熵使用方面可能会有些浪费。出于性能原因,它将从/ dev / random 一次读取nextBytes()nextInt()的32字节数据。在100毫秒内未被使用的任何数据将被丢弃。因此,对nextBoolean()的每次调用都会给出一位输出,但是实现可能实际上会使用256位的熵来(并且大部分丢弃)。

SecureRandom.getInstanceStrong()方法

此方法使用java.security文件中securerandom.strongAlgorithms属性来选择一个SecureRandom实现。

如果您只是使用旧机制获得最高优先级的SecureRandom实现(即新的java.security.SecureRandom()),那么您将根据您的平台获得以下实现:

[表“2”未找到/]

如果您使用SecureRandom.getInstanceStrong(),则使用以下默认值:

[表“3”未找到/]

由于这种默认行为,您应该避免在可用性很重要的Solaris / Linux / MacOS上运行的任何服务器端代码中使用SecureRandom.getInstanceStrong()


当为了加密而在Java中生成随机数时,许多开发人员经常使用java.security.SecureRandom该类虽然java.security.SecureRandom该类旨在生成加密安全的随机数,但API中有几个细微之处,如果使用不当,则输出可以变得可预测。在Cigital,我们见证了一些这样的情况。以下是正确使用Java java.security.SecureRandom类的指南。

首先,我们来看看java.security.SecureRandomAPI的工作原理。java.security.SecureRandom类没有真正实现一个伪随机数生成器(PRNG)本身。它在其他类中使用PRNG实现来生成随机数。java.security.SecureRandom创建实例时,实际可以使用一些实际的PRNG PRNG是Java加密服务提供商(CSP)的一部分。在Sun的Java实现中,默认情况下使用SUN CSP。在Windows上,SUN CSP默认使用在sun.security.provider.SecureRandom中实现的SHA1PRNG。在Solaris和Linux上,SUN CSP默认是使用sun.security.provider.NativePRNG,它只提供操作系统提供的/dev/urandomPRNG 的输出但是,如果在Solaris / Linux上,则会修改JRE中的java.security配置文件,以便securerandom。/dev/urandom源设置为除文件之外的其他东西: 然后在Windows上使用sun.security.provider.SecureRandom中实现的SHA1PRNG。当然,应用程序可以选择不使用默认值,并且可以随时指定由特定加密提供程序实现的特定PRNG。在Cigital的经验中,大多数Java应用程序java.security.SecureRandom实际上使用了SUN CSP提供的SHA1PRNG。

现在,应用程序最终会sun.security.provider.SecureRandom使用以下调用实现的SHA1PRNG 实例

//以下将在Windows上创建SUN SHA1PRNG 
//默认配置和Sun JRE,以及Solaris / Linux
//如果在java.security中修改securerandom.source
SecureRandom sr1 = new SecureRandom();

//以下将创建SUN SHA1PRNG如果最高 
//优先级CSP是SUN
SecureRandom sr2 = SecureRandom.getInstance(“SHA1PRNG”);

//以下将始终创建SUN SHA1PRNG
SecureRandom sr3 = SecureRandom.getInstance(“SHA1PRNG”,“SUN”);

请注意,根据Sun的文档,java.security.SecureRandom任何这些调用都不会返回返回的实例。如果在这些调用中的一个java.security.SecureRandom.nextBytes(byte[])被调用之后,则使用由底层操作系统提供的安全机制(从Windows中的JRE 1.4.1开始,在Linux和Solaris中以JRE 1.4.2开始)种下PRNG。如果在调用之前java.security.SecureRandom.setSeed(long)java.security.SecureRandom.setSeed(byte[])之后调用java.security.SecureRandom.nextBytes(byte[]),则旁路内部播种机制,只有提供的种子才能生成随机数。

对于那些不熟悉加密PRNG的内部工作的人来说,他们的工作就是采取一个相对较小的随机种子,并使用它来产生对于不知道种子是什么的人来说似乎随机的确定性输出。PRNG试图确保输出不显示有关种子的任何信息,并且观察输出的人不能在不知道种子的情况下预测未来的输出。

通过绕过SHA1PRNG的内部安全播种机制,您可能会危及PRNG输出的安全性。如果您使用攻击者可能预测的任何内容(例如,创建PRNG实例的时间),那么使用java.security.SecureRandom可能无法提供所需的安全级别。

最后,无论PRNG如何播种,不应无限期地使用PRNG。PRNG输出的长期安全性有两种方法:

  • 定期扔掉现有的java.security.SecureRandom实例并创建一个新的实例。这将生成一个新的实例与一个新的种子。
  • 通过调用java.security.SecureRandom.setSeedjava.security.SecureRandom.generateSeed(int)来定期向PRNG种子添加新的随机资料

总之,使用时请注意以下几点java.security.SecureRandom

  • 始终指定您要使用的确切PRNG和提供商。如果您只使用默认PRNG,则可能需要在不同的应用程序安装中使用不同的PRNG,以便正常工作。使用以下代码获取PRNG实例是适当的:
    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "SUN");
  • 使用SHA1PRNG时,java.security.SecureRandom.nextBytes(byte[])在创建新的PRNG实例后,请始终立即调用这将使PRNG牢牢扎根。如果出于测试目的,您需要可预测的输出,忽略此规则并且使用硬编码/可预测值播种PRNG可能是适当的。
  • 至少在Windows上使用JRE 1.4.1,至少在Solaris和Linux上使用JRE 1.4.2。早期版本不能安全地播放SHA1PRNG。
  • 观察使用一个种子生成的大量PRNG输出可以定期重新设定您的PRNG,这样可以让攻击者确定种子,从而预测未来的所有输出。


你可能感兴趣的:(JDK8,java8)