SecureRandom.getInstance("SHA1PRNG", "SUN")一直阻塞而new SecureRandom()却不会?

7
我希望咨询关于Java中SecureRandom常见神话的一些问题,即安全与性能的权衡。我在互联网上进行了一段时间的研究,并整理了以下信息。我希望这里的人们可以帮助我确认我的研究成果,并希望能够得到一些有关实际选择实现的想法。基本上,以下是一些最受欢迎和详尽的SecureRandom文章:Proper use of Java's SecureRandom:https://www.synopsys.com/blogs/software-security/proper-use-of-javas-securerandom/; Issues when using Java's SecureRandom:https://www.synopsys.com/blogs/software-security/issues-when-using-java-securerandom/; Using the SecureRandom class:http://moi.vonos.net/java/securerandom/

现在Java 8已经发布,Sun官方已经承认了一个bug/混乱,并提出了解决方案:http://openjdk.java.net/jeps/123

但是通过查看文档,我并不确定这个问题是否得到了很好的解决:http://docs.oracle.com/javase/8/docs/api/java/security/SecureRandom.html

因此,根据Amit Sethi的建议,人们应该使用指定实例化方式,例如:SecureRandom sr3 = SecureRandom.getInstance("SHA1PRNG", "SUN")。然而,事实上,Sun告诉我们这将总是从/dev/random中读取(???),这意味着它可能会阻塞每个调用。相反,如果您使用new SecureRandom(),它将始终从/dev/urandom中读取,除非调用generateSeed()。详情请参见:

http://bugs.java.com/view_bug.do?bug_id=6202721

这是否意味着在当前Java中仍然推荐使用"new SecureRandom()"?我找到的其他文档没有明确说明这一点,所以我想知道这是否仍然正确?
现在,如果选择"new SecureRandom()"并将导致不阻塞调用,那么我认为应该为定期重新生成做些什么:
将SecureRandom作为类中的静态实例,并让另一个Executor线程定期调用它的generateSeed(),因此即使调用会阻塞,也不会影响我的应用程序中的主请求处理线程。这样做听起来像个好方法吗?
非常感谢任何Java和加密专家在这里解决这个问题。
编辑: 这里还有另一个有用的线程,似乎支持我的猜测:https://bugs.openjdk.java.net/browse/JDK-4705093

1
请澄清一下,您的实际问题是什么?似乎有几个问题。 - user207421
2个回答

4
编辑:首先,如果/dev/random/dev/urandom块,那么首先尝试解决特定问题是有意义的。以下解决方案涉及尝试修复Java中的SecureRandom,因此它不太依赖于那些特殊设备。
使用nextBytes()作为内部调用generateSeed()使用种子信息的原始提供程序。换句话说,您也可以创建一个单独的SecureRandom实例。请注意,每当您拥有具有足够高状态和良好后端算法的SecureRandom实例时,不需要经常进行重新播种(因为极不可能创建循环)。如果您需要更高的熵,请每次生成一个新的随机类,或者使用使用SecureRandom.getInstanceStrong()检索到的随机数。
通常最好使用默认的new SecureRandom()。虽然在加密算法(例如"AES/CBC/PKCS5Padding")上应始终提供精确的算法名称,但对于安全随机功能,最好让系统确定哪个算法最佳。如果您想更改任何内容,请通过命令行结构进行更改,并确保在Unix系统上可用/dev/urandom
如果仍然存在阻止问题,则创建一个中央的、系统种子的SecureRandom。使用nextBytes()创建新的种子材料,并将其提供给使用SecureRandom(byte[] seed)构造函数的新SecureRandom。只有在new SecureRandom()无法处理该情况时才应使用此方法。
即使现在通过使用构造函数显式提供了初始种子,但构造函数本身并不保证它仅用于播种RNG。但是可能是这种情况,因此阻塞的可能性较小。SecureRandom线程安全的,因此您无需同步访问它。
使用新的Java 8 SecureRandom.getInstanceStrong() 更可能阻塞。我认为他们添加了这个功能是为了使默认实例非阻塞。RSA密钥对生成通常需要大量熵,因此返回的实例可能使用阻塞调用到/dev/random
总的来说,仍然相当混乱地使用SecureRandom进行特殊情况处理。幸运的是,这只是极少数用例的问题。

感谢这里的信息。不好意思,能否让我再明确一下... 我需要高安全性 --- 因此需要定期重新播种(除非这只是无谓的担心?),同时对于任何调用都不会阻塞。使用timertask类型的东西来做播种工作有什么缺点吗?根据我的理解,像"new SecureRandom()"这样创建“新实例”永远不会进行重新播种,即使它是一个“新实例”。但是,使用指定算法的getInstance调用,或者像您提到的使用“getInstanceStrong()”,很可能会导致阻塞,这是不希望的...在这里究竟什么是最好的方法? - Superziyi
1
做好它是一个平衡的行为。不过,你有一个重要的优势;一旦PRNG被良好地种子化,你可以在它循环之前(到达先前的状态,因此它将重复随机字节)从中请求大量数据。通常这大约是内部状态大小的一半。对于SHA-1来说,这仍然是2^80轮,一个可怕的大数字。因此,原则上你不需要重新种子化。但是,如果你担心内部状态被破坏,例如由于侧信道信息,你可以重新种子化。随机数质量始终是一个棘手的问题。 - Maarten Bodewes
谢谢。我想这里并没有"绝对正确的方法",正如你所说,它都是关于平衡和权衡。可能我的确低估了不重新播种提供的安全性,因为人们通常这样做 :-) 我会选择一个设计并让我的同事帮助审核。无论如何,对于在寻找相同信息的人来说,这里有很好的信息和思路,我将标记你的回答为已接受。 - Superziyi

0

您可以使用haveged生成熵来减少阻塞。这在云托管服务器中特别有用,因为熵源较少。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接