如何解决缓慢的JavaSecureRandom?
如果我们希望使用Java加密强度高的随机数,请使用SecureRandom
。不幸的是,SecureRandom
可能非常慢。如果在Linux上使用/ dev / random
,它将阻止等待足够的熵建立。我们如何避免性能下降?
有没有人使用"罕见数学"作为该问题的解决方案?
任何人都可以确认在JDK 6中已经解决了此性能问题吗?
解决方案
我自己还没有遇到这个问题,但是我会在程序启动时生成一个线程,该线程立即尝试生成一个种子,然后死亡。我们调用随机数的方法将在该线程处于活动状态时加入该线程,因此,只有在程序执行的很早才发生时,第一次调用才会阻塞。
我们所提到的关于/ dev / random的问题不是SecureRandom算法的问题,而是它所使用的随机性的来源。两者是正交的。我们应该找出两者中的哪一个正在减慢速度。
我们明确链接的"罕见的数学"页面提到它们没有解决随机性的问题。
我们可以尝试使用不同的JCE提供程序,例如BouncyCastle,以查看它们对SecureRandom
的实现是否更快。
简短的搜索还显示了用Fortuna替换默认实现的Linux修补程序。我对此一无所知,但是欢迎我们进行调查。
我还应该提到,尽管使用不良实现的SecureRandom
算法和/或者随机性源非常危险,但是我们可以使用SecureRandomSpi
的自定义实现来滚动自己的JCE Provider。我们将需要与Sun进行合作才能使提供商签名,但这实际上非常简单。他们只需要我们传真给他们一张表格,说明我们知道美国对加密库的出口限制。
如果我们想要真正的随机数据,那么不幸的是我们必须等待它。这包括SecureRandom
PRNG的种子。罕见的数学无法以比" SecureRandom"更快的速度收集真正的随机数据,尽管它可以连接到互联网以从特定网站下载种子数据。我的猜测是,这不可能比可用的/ dev / random
更快。
如果我们想要PRNG,请执行以下操作:
SecureRandom.getInstance("SHA1PRNG");
支持哪些字符串取决于" SecureRandom" SPI提供程序,但是我们可以使用" Security.getProviders()"和" Provider.getService()"来枚举它们。
Sun喜欢SHA1PRNG,因此可以广泛使用。随着PRNG的发展,它并不是特别快,但是PRNG只是计算数字,不会阻碍对熵的物理测量。
唯一的例外是,如果我们在获取数据之前未调用setSeed()
,则PRNG将在我们首次调用next()
或者nextBytes()
时对其自身进行播种。通常,它会使用来自系统的相当少量的真实随机数据来执行此操作。此调用可能会阻止,但将使随机数源比"将当前时间与PID一起哈希,加27并希望达到最佳效果"的任何变体更为安全。但是,如果我们需要的只是游戏的随机数,或者如果我们希望将来使用同一种子进行测试以使流可重复使用,那么不安全的种子仍然有用。
我的经验仅在于PRNG的缓慢初始化,而不是之后的随机数据生成。尝试更急切的初始化策略。由于创建它们很昂贵,因此将其视为单例并重用同一实例。如果一个实例的线程争用过多,则将它们合并或者使它们成为线程本地的。
不要妥协随机数的产生。那里的弱点危及所有安全性。
我看不到很多基于COTS原子衰变的生成器,但是如果我们确实需要大量随机数据,则有一些针对它们的计划。约翰·沃克(John Walker)的Fourmilab是一个总是值得关注的站点,其中包括HotBits。
在Linux上," SecureRandom"的默认实现是" NativePRNG"(此处为源代码),这往往很慢。在Windows上,默认值为" SHA1PRNG",正如其他人指出的那样,如果我们明确指定,也可以在Linux上使用。
" NativePRNG"与" SHA1PRNG"不同,它与Uncommons Maths的AESCounterRNG的不同之处在于,它不断从操作系统接收熵(通过从" / dev / urandom"读取)。播种后,其他PRNG不会获得任何其他熵。
AESCounterRNG比" SHA1PRNG"快10倍,而" SHA1PRNG"的IIRC本身比" NativePRNG"快2到3倍。
如果我们需要一个更快的PRNG,以便在初始化后获取熵,请查看是否可以找到Fortuna的Java实现。 Fortuna实现的核心PRNG与AESCounterRNG所使用的PRNG相同,但是也有一个复杂的熵池和自动播种系统。
使用安全随机数作为循环算法的初始化源;我们可以使用梅森捻线机进行批量工作,而不是使用UncommonMath中的那种捻线机,这种方式已经存在了一段时间,并且比其他产品更胜一筹。
http://en.wikipedia.org/wiki/梅森_特威斯特(Mersenne_twister)
确保立即刷新用于初始化的安全随机数,例如,我们可以为每个客户端生成一个安全随机数,每个客户端使用一个mersenne twister伪随机生成器,以获得足够高的随机化程度
如果我们想要真正的"密码学上强大的"随机性,那么我们需要一个强大的熵源。 / dev / random速度很慢,因为它必须等待系统事件收集熵(磁盘读取,网络数据包,鼠标移动,按键等)。
更快的解决方案是硬件随机数生成器。我们可能已经在主板上内置了一个主板。请查阅hw_random文档,以获取有关确定是否拥有它以及如何使用它的说明。 rng-tools软件包包括一个守护程序,它将把硬件产生的熵输入到/ dev / random中。
如果HRNG在系统上不可用,并且我们愿意牺牲熵强度来提高性能,那么我们将希望使用/ dev / random
中的数据来播种良好的PRNG,并让PRNG进行大部分工作。在SP800-90中列出了几个NIST批准的PRNG,它们很容易实现。
听起来我们应该更加了解RNG要求。最强的加密RNG要求(据我所知)是,即使我们知道用于生成它们的算法,并且知道所有以前生成的随机数,也无法获得有关在生成的随机数中有用的任何信息。未来,而无需花费不切实际的计算能力。
如果我们不需要这种完全的随机性保证,则可能需要进行适当的性能折衷。我倾向于同意Dan Dyer对Uncommons-Maths或者Fortuna的AESCounterRNG的回应(其作者之一是密码学专家Bruce Schneier)。我从未使用过,但是乍一看这些想法似乎很有名。
我认为如果我们可以定期生成初始随机种子(例如,每天或者每小时一次或者任何其他时间),则可以使用快速流密码从流的连续块中生成随机数(如果流密码使用XOR,那么传入null数据流或者直接获取XOR位)。 ECRYPT的eStream项目具有许多良好的信息,包括性能基准。这不会在我们补充时间的时间点之间保持熵,因此,如果有人知道随机数和我们使用的算法之一,从技术上讲,可能有可能以强大的计算能力来破坏流密码和猜测其内部状态,以便能够预测未来的随机数。但是我们必须决定这种风险及其后果是否足以证明维持熵的成本是合理的。
编辑:这是一些我在网上找到的有关RNG的加密课程笔记,这些笔记与该主题非常相关。
我们应该能够使用以下命令在Linux上选择速度更快但安全性稍差的/ dev / urandom:
-Djava.security.egd=file:/dev/urandom
但是,这不适用于Java 5和更高版本(Java错误6202721)。建议的解决方法是使用:
-Djava.security.egd=file:/dev/./urandom
(注意额外的" /./")