如果您想要Java中具有加密强度的随机数,请使用SecureRandom
。不幸的是,SecureRandom
可能会很慢。如果/dev/random
在Linux上使用,它将阻止等待足够的熵建立。您如何避免性能下降?
有没有人使用罕见数学作为解决此问题的方法?
有人可以确认在JDK 6中已经解决了此性能问题吗?
如果您想要Java中具有加密强度的随机数,请使用SecureRandom
。不幸的是,SecureRandom
可能会很慢。如果/dev/random
在Linux上使用,它将阻止等待足够的熵建立。您如何避免性能下降?
有没有人使用罕见数学作为解决此问题的方法?
有人可以确认在JDK 6中已经解决了此性能问题吗?
Answers:
如果您想要真正的随机数据,那么不幸的是您必须等待它。这包括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并希望达到最佳效果”的任何变体更为安全。但是,如果您需要的只是游戏的随机数,或者您希望将来使用同一种子进行测试以使流可重复使用,那么不安全的种子仍然有用。
您应该能够使用以下命令在Linux上选择速度更快但安全性稍差的/ dev / urandom:
-Djava.security.egd=file:/dev/urandom
但是,这不适用于Java 5和更高版本(Java错误6202721)。建议的解决方法是使用:
-Djava.security.egd=file:/dev/./urandom
(请注意额外的/./
)
/dev/urandom
,Sun仍将其视为魔术字符串并使用/dev/random
,因此您必须伪造它。当一个file:
URL不是一个file:
URL?只要Sun决定不是:-(
file:/dev/urandom
中-Djava.security.egd
或在其中都securerandom.source
进行了设置/dev/random/
)仍然可以在任何时候读取SecureRandom.getSeed()
(或setSeed()
调用)。解决方法file:/dev/./urandom
导致根本无法读取/dev/random
(已通过strace确认)
/dev/urandom/
是,如果您使用它在开箱即用的新硬件上生成机密,可能会发生完全可预测的情况,那将会发生什么。/dev/urandom/
即使那是您应该采取的一种情况,也不会阻止熵。如果机密信息是永久性的,情况就更糟了,例如设备在首次启动时所做的第一件事就是生成一个公钥-私钥对。在那些可怕的情况之外,无论如何,/dev/urandom
总比使用通用SecureRandom
算法更好。
在Linux上,的默认实现SecureRandom
是NativePRNG
(此处的源代码),这通常很慢。在Windows上,默认值为SHA1PRNG
,正如其他人指出的那样,如果您明确指定,也可以在Linux上使用。
NativePRNG
不同于SHA1PRNG
并罕见Maths's AESCounterRNG的地方在于,它不断从操作系统接收熵(通过从/dev/urandom
)。播种后,其他PRNG不会获得任何其他熵。
AESCounterRNG比快大约10倍SHA1PRNG
,而IIRC本身比快2到3倍NativePRNG
。
如果您需要一个更快的PRNG,以便在初始化后获取熵,请查看是否可以找到Fortuna的Java实现。Fortuna实现的核心PRNG与AESCounterRNG所使用的PRNG相同,但是也有一个复杂的熵池和自动补种系统。
许多Linux发行版(大多基于Debian)将OpenJDK配置为/dev/random
用于熵。
/dev/random
根据定义,它很慢(甚至可以阻止)。
在这里,您有两种方法可以解除锁定:
选项1,改善熵
要获取更多信息/dev/random
,请尝试使用hadged守护程序。它是一个守护程序,可以连续收集HAVEGE熵,并且还可以在虚拟化环境中工作,因为它不需要任何特殊的硬件,只需要CPU本身和时钟。
在Ubuntu / Debian上:
apt-get install haveged
update-rc.d haveged defaults
service haveged start
在RHEL / CentOS上:
yum install haveged
systemctl enable haveged
systemctl start haveged
选项2.减少随机性要求
如果由于某种原因上述解决方案无济于事,或者您不关心加密强度大的随机性,则可以改用,以/dev/urandom
确保不会阻塞。
要全局执行此操作jre/lib/security/java.security
,请在默认Java安装中编辑该文件以使用/dev/urandom
(由于另一个错误,该文件需要指定为/dev/./urandom
)。
像这样:
#securerandom.source=file:/dev/random
securerandom.source=file:/dev/./urandom
然后,您将不必在命令行上指定它。
注意:如果进行加密,则需要良好的熵。案例分析-android PRNG问题降低了比特币钱包的安全性。
/dev/random
从定义上讲是缓慢的(甚至可以阻止)”是错误的;它完全取决于系统配置。较新的机器可能有例如,可以使用的CPU快速RNG和BSD机一般具有相同的实现/dev/random
和/devl/urandom
。不过,您可能不一定一定要依赖 /dev/random
快速。在VM上,您可能需要在客户端VM上安装客户端工具集,以便可以使用主机OS的RNG。
如果您想要真正的“密码学上强”的随机性,那么您需要一个强大的熵源。/dev/random
之所以慢,是因为它必须等待系统事件来收集熵(磁盘读取,网络数据包,鼠标移动,按键等)。
更快的解决方案是硬件随机数生成器。您可能已经在主板上内置了一个主板。请查阅hw_random文档,以获取有关确定是否拥有它以及如何使用它的说明。rng-tools软件包包括一个守护程序,它将把硬件生成的熵输入/dev/random
。
如果HRNG在您的系统上不可用,并且您愿意牺牲熵强度来提高性能,那么您将希望使用来提供良好的PRNG数据/dev/random
,并让PRNG进行大部分工作。SP800-90中列出了几个NIST批准的PRNG, 它们易于实现。
使用Java 8,我发现在Linux上调用SecureRandom.getInstanceStrong()
会给我NativePRNGBlocking
算法。这通常会阻塞许多秒钟,以生成一些字节的盐。
我改用显式要求NativePRNGNonBlocking
,并且正如名称中所期望的那样,它不再受阻。我不知道这对安全有何影响。大概非阻塞版本不能保证所使用的熵的数量。
更新:好的,我找到了一个很好的解释。
简而言之,为避免阻塞,请使用new SecureRandom()
。这会使用/dev/urandom
,它不会阻塞,并且基本上和一样安全/dev/random
。在帖子中:“您唯一想调用/ dev / random的时间是在计算机首次启动时,并且熵尚未累积”。
SecureRandom.getInstanceStrong()
为您提供绝对最强的RNG,但只有在一堆阻塞不会影响您的情况下,它才是安全的。
getInstanceStrong()
使用长期密钥,例如TLS证书的密钥。而且即使那样,我还是更愿意使用new SecureRandom()
兼容FIPS的密钥对生成器或随机数生成器。因此,是的,这提供了一个答案,如果 /dev/urandom
不被阻止的话:最终它仍然仍然依赖于系统熵。但总体而言,这是非常好的建议。如果/dev/urandom
阻塞,则可能必须修复问题的根源,而不是Java应用程序。
有一个工具(至少在Ubuntu上)可以将人为的随机性提供给您的系统。该命令很简单:
rngd -r /dev/urandom
并且您可能需要在前面安装sudo。如果没有rng-tools软件包,则需要安装它。我尝试了一下,它肯定对我有帮助!
资料来源:马特与世界
sudo rngd -r /dev/urandom
使用sudo apt install rng-tools
我遇到了同样的问题。在搜索了正确的搜索词之后,我遇到了关于DigitalOcean的这篇不错的文章。
我只是在这里引用文章的相关部分。
基于HAVEGE原理,并且先前基于其关联的库,hedge允许根据处理器上代码执行时间的变化生成随机性。由于即使在相同的环境下,在相同的硬件上,也几乎不可能用同一代码执行相同的时间,因此运行单个或多个程序的时机应该适合于植入随机源。重复执行循环后,使用漏洞的实现会使用处理器时间戳计数器(TSC)中的差异为系统的随机源(通常是/ dev / random)播种
您所参考的问题/dev/random
不在于SecureRandom
算法,而在于它使用的随机性来源。两者是正交的。您应该找出两者中的哪一个正在减慢您的速度。
您明确链接的“罕见的数学”页面提到它们没有解决随机性问题。
您可以尝试使用不同的JCE提供程序,例如BouncyCastle,以查看它们的实现SecureRandom
是否更快。
简短的搜索还显示了用Fortuna替换默认实现的Linux补丁。我对此了解不多,但是欢迎您进行调查。
我还应该提到,虽然使用执行SecureRandom
不当的算法和/或随机性源非常危险,但是您可以使用的自定义实现来滚动自己的JCE Provider SecureRandomSpi
。您将需要与Sun进行合作才能使您的提供商签名,但这实际上非常简单。他们只需要您传真给他们一张表格,说明您知道美国对加密库的出口限制。
使用安全随机数作为循环算法的初始化源;您可以使用梅森捻线机进行批量工作,而不是使用UncommonMath中的那种捻线机,这种方式已经存在了一段时间,并且比其他产品更胜一筹。
http://en.wikipedia.org/wiki/梅森_twister
确保立即刷新用于初始化的安全随机数,例如,您可以为每个客户端生成一个安全随机数,每个客户端使用一个mersenne twister伪随机生成器,以获得足够高的随机化程度
Random
,但不是SecureRandom
。
根据文档,按优先顺序,SecureRandom使用的不同算法是:
自从您询问Linux以来,我就忽略了Windows的实现,也忽略了SunPKCS11(仅在Solaris上才真正可用),除非您自己安装了它-然后您就不会再问这个了。
根据这些相同的文档,这些算法使用的是
SHA1PRNG
当前,初始播种是通过系统属性和java.security熵收集设备的组合完成的。
NativePRNG
nextBytes()
使用/dev/urandom
generateSeed()
用途/dev/random
NativePRNG封锁
nextBytes()
与generateSeed()
使用/dev/random
NativePRNGNon阻止
nextBytes()
和generateSeed()
使用/dev/urandom
这意味着,如果您使用SecureRandom random = new SecureRandom()
,它将从该列表中删除,直到找到一个有效的列表,该列表通常为NativePRNG。这意味着它从中获得种子/dev/random
(或者,如果您明确生成了种子,则使用该种子),然后/dev/urandom
用于获取下一个字节,整数,双精度,布尔值,您拥有什么。
由于/dev/random
正在阻塞(阻塞直到它在熵池中具有足够的熵),这可能会妨碍性能。
一种解决方案是使用诸如Haveged之类的方法来生成足够的熵,而另一种解决方案是使用/dev/urandom
。虽然您可以为整个jvm设置它,但是更好的解决方案是使用对此特定实例SecureRandom
进行处理SecureRandom random = SecureRandom.getInstance("NativePRNGNonBlocking")
。请注意,如果NativePRNGNonBlocking,则该方法可能引发NoSuchAlgorithmException,因此请准备回退到默认值。
SecureRandom random;
try {
random = SecureRandom.getInstance("NativePRNGNonBlocking");
} catch (NoSuchAlgorithmException nsae) {
random = new SecureRandom();
}
另请注意,在其他* nix系统上,其/dev/urandom
行为可能有所不同。
/dev/urandom
随机就够了吗?传统观点认为,这仅仅/dev/random
是随机的。但是,有些声音有所不同。在“使用SecureRandom的正确方法”和“关于/ dev / urandom的神话”中,论证了/dev/urandom/
同样的好。
信息安全堆栈上的用户对此表示赞同。基本上,如果您需要询问,/dev/urandom
就可以满足您的目的。
我自己还没有遇到这个问题,但是我会在程序启动时生成一个线程,该线程立即尝试生成种子,然后死亡。您调用随机数的方法将在该线程处于活动状态时加入该线程,因此,只有在程序执行的很早才发生时,第一次调用才会阻塞。
我的经验仅在于PRNG的缓慢初始化,而不是之后的随机数据生成。尝试更急切的初始化策略。由于创建起来很昂贵,因此将其视为单例并重复使用同一实例。如果一个实例的线程争用过多,则将它们合并或使其成为线程本地的。
不要妥协随机数的产生。那里的弱点危及您的所有安全性。
我看不到很多基于COTS原子衰减的生成器,但是如果您确实需要大量随机数据,则有一些针对它们的计划。约翰·沃克(John Walker)的Fourmilab是一个总是值得关注的站点,包括HotBits。
SecureRandom
在过去10年的熵收集中已发生了两次更改。
SecureRandom
仍然不是问题的可能性,但是系统中的低熵将始终是问题。使用单例将创建强耦合代码,这是一种设计反模式。因此,应格外小心地使用它;如果您要解决此问题,则最好必须反转代码中的所有引用。
听起来您应该更加了解RNG要求。最强的加密RNG要求(据我所知)是,即使您知道用于生成它们的算法,并且知道所有以前生成的随机数,也无法获得有关在生成的随机数中有用的任何信息。未来,而无需花费不切实际的计算能力。
如果您不需要完全的随机性保证,则可能需要进行适当的性能折衷。我倾向于同意Dan Dyer对 Uncommons-Maths或Fortuna的AESCounterRNG 的回应(其作者之一是密码学专家Bruce Schneier)。我从未使用过,但乍一看这些想法似乎很有名。
我认为如果您可以定期生成初始随机种子(例如,每天或每小时一次或任何其他时间),则可以使用快速流密码从流的连续块生成随机数(如果流密码使用XOR,那么传递空值流或直接获取XOR位)。ECRYPT的 eStream该项目有很多很好的信息,包括性能基准。这不会在您补充时间的各个时间点之间保持熵,因此,如果有人知道随机数和您使用的算法之一,从技术上讲,它有可能以强大的计算能力来破坏流密码和猜测其内部状态,以便能够预测未来的随机数。但是,您必须决定这种风险及其后果是否足以证明维持熵的成本合理。
如果您的硬件支持,请尝试使用我是作者的Java RdRand Utility。
它基于Intel的RDRAND
指令,比SecureRandom
大规模实施快约10倍,并且没有带宽问题。
注意,该实现仅在提供指令的那些CPU上有效(即,设置rdrand
处理器标志时)。您需要通过RdRandRandom()
构造函数显式实例化它。没有具体的Provider
实现。
RDRAND
是一个很好的来源,但它有点不可信。它肯定需要成为收藏家的众多投入之一(对David Johnston丝毫没有冒犯)。