如何处理缓慢的SecureRandom生成器?


165

如果您想要Java中具有加密强度的随机数,请使用SecureRandom。不幸的是,SecureRandom可能会很慢。如果/dev/random在Linux上使用,它将阻止等待足够的熵建立。您如何避免性能下降?

有没有人使用罕见数学作为解决此问题的方法?

有人可以确认在JDK 6中已经解决了此性能问题吗?


看来这与SecureRandom.generateSeed()的速度慢有关。有一个被拒绝的缺陷解释了这种缓慢性和解决方法:JDK-6521844:SecureRandom挂在Linux系统上
AlikElzin-kilaka,2016年

检出/ dev / urandom(不是/ dev / random)..如果存在阻塞问题,请考虑仅从urandom获取随机数生成器种子。
jcalfee314

Answers:


79

如果您想要真正的随机数据,那么不幸的是您必须等待它。这包括SecureRandomPRNG 的种子。罕见的数学无法以比更快的速度收集真正的随机数据SecureRandom,尽管它可以连接到互联网以从特定网站下载种子数据。我的猜测是,这不可能比可用的速度更快/dev/random

如果您想要PRNG,请执行以下操作:

SecureRandom.getInstance("SHA1PRNG");

支持哪些字符串取决于SecureRandomSPI提供程序,但是您可以使用Security.getProviders()和枚举它们Provider.getService()

Sun喜欢SHA1PRNG,因此可以广泛使用。随着PRNG的发展,它并不是特别快,但是PRNG只是计算数字,不会阻碍对熵的物理测量。

唯一的例外是,如果您setSeed()在获取数据之前不进行调用,则PRNG会在您首次调用next()或时提供种子nextBytes()。通常,它将使用系统中少量的真实随机数据来执行此操作。此调用可能会阻止,但将使您的随机数源比“将当前时间与PID一起哈希,加27并希望达到最佳效果”的任何变体更为安全。但是,如果您需要的只是游戏的随机数,或者您希望将来使用同一种子进行测试以使流可重复使用,那么不安全的种子仍然有用。


“罕见数学”仅从Internet下载数据作为种子,在生成随机数时不会返回该随机数据。
Dan Dyer's

与SecureRandom相同-/ dev / urandom仅用于播种。
AviD

是的 当发问者说“如果您想要一个随机数时,请使用SecureRandom-这可能很慢”,我想也许他正在对所有内容使用getSeed并耗尽了他的熵池。解决方法不是获得JDK 6,而是按其预期的方式使用SecureRandom ;-)
Steve Jessop

@Dan Dyer-我已更正有关“罕见数学”的评论。我确实看过您的页面,所以我知道“随机数”的意思是“为其种子”而不是“返回给用户”。但是你说的没错,我不是说...
Steve Jessop

“它广泛可用”。它是否包含在每个兼容的JDK中?这是Java安全标准名称的列表...(docs.oracle.com/javase/8/docs/technotes/guides/security/...
肖恩·赖利

176

您应该能够使用以下命令在Linux上选择速度更快但安全性稍差的/ dev / urandom:

-Djava.security.egd=file:/dev/urandom

但是,这不适用于Java 5和更高版本(Java错误6202721)。建议的解决方法是使用:

-Djava.security.egd=file:/dev/./urandom

(请注意额外的/./


24
请注意,Java错误报告显示“不是缺陷”。换句话说,即使默认值为/dev/urandom,Sun仍将其视为魔术字符串并使用/dev/random,因此您必须伪造它。当一个file:URL不是一个file:URL?只要Sun决定不是:-(
Jim Garrison

6
刚刚花了很多时间对此进行了调查,似乎普通的设置(即使在java.security文件file:/dev/urandom-Djava.security.egd或在其中都securerandom.source进行了设置/dev/random/)仍然可以在任何时候读取SecureRandom.getSeed()(或setSeed()调用)。解决方法file:/dev/./urandom导致根本无法读取/dev/random(已通过strace确认)
matt b

7
/dev/urandom并没有比/dev/random使用现代CSPRNG实施时更安全:en.wikipedia.org/wiki//dev/random#FreeBSD
lapo 2012年

我认为主要的担心/dev/urandom/是,如果您使用它在开箱即用的新硬件上生成机密,可能会发生完全可预测的情况,那将会发生什么。/dev/urandom/即使那是您应该采取的一种情况,也不会阻止熵。如果机密信息是永久性的,情况就更糟了,例如设备在首次启动时所做的第一件事就是生成一个公钥-私钥对。在那些可怕的情况之外,无论如何,/dev/urandom总比使用通用SecureRandom算法更好。
史蒂夫·杰索普

1
哪一个是正确的?-Djava.security.egd = file:/ dev /./ urandom或file:/// dev / urandom @mattb
Aarish Ramesh

35

在Linux上,的默认实现SecureRandomNativePRNG此处的源代码),这通常很慢。在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相同,但是也有一个复杂的熵池和自动补种系统。


此链接不起作用。uncommons-maths.dev.java.net/nonav/api/org/uncommons/maths/…。有什么我可以看到的吗?
UVM 2012年

@Unni刚刚更新了链接。请注意,我在此答案中提出的性能要求可能不再有效。我认为在Java的最新版本中情况可能会有所改善,并且平台之间的性能可能有所不同(即Windows与Liux)。
丹·代尔

我只运行了一个带MessageDigest的SecureRandom示例,并对其进行了十六进制编码。我的Windows 7 PC上的整个操作花费了33毫秒。这是一个问题。字符串randomNum = new Integer(prng.nextInt()).toString(); MessageDigest sha = MessageDigest.getInstance(“ SHA-1”);结果= sha.digest(randomNum.getBytes()); str = hexEncode(结果);
2012年

24

许多Linux发行版(大多基于Debian)将OpenJDK配置为/dev/random用于熵。

/dev/random 根据定义,它很慢(甚至可以阻止)。

在这里,您有两种方法可以解除锁定:

  1. 改善熵,或
  2. 降低随机性要求。

选项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。
Maarten Bodewes,

17

SecureRandom在无头Debian服务器上一次调用约25秒来阻塞也遇到了类似的问题。我安装了haveged守护程序,以确保/dev/random在不停顿的服务器上保持充值状态,您需要像这样的东西来生成所需的熵。我SecureRandom现在打来的电话可能要花费毫秒。


4
apt-get install hadged,然后update-rc.d Haveged默认值
Rod Lima

11

如果您想要真正的“密码学上强”的随机性,那么您需要一个强大的熵源。/dev/random之所以慢,是因为它必须等待系统事件来收集熵(磁盘读取,网络数据包,鼠标移动,按键等)。

更快的解决方案是硬件随机数生成器。您可能已经在主板上内置了一个主板。请查阅hw_random文档,以获取有关确定是否拥有它以及如何使用它的说明。rng-tools软件包包括一个守护程序,它将把硬件生成的熵输入/dev/random

如果HRNG在您的系统上不可用,并且您愿意牺牲熵强度来提高性能,那么您将希望使用来提供良好的PRNG数据/dev/random,并让PRNG进行大部分工作。SP800-90中列出了几个NIST批准的PRNG, 它们易于实现。


很好,但是我的代码是商业应用程序的一部分。我对服务器环境没有任何控制权。我认为目标服务器始终没有鼠标和键盘,并且完全依靠磁盘和网络I / O来实现熵,这可能是根本问题。
David G

3
我发现的/ dev /随机依赖于系统事件,所以作为暂时的解决办法,我只是提出我的鼠标来回,而我的测试跑....
David K制作

用于i820芯片组的82802集线器非常慢(RIP)。我很惊讶您可以从中收集到任何有用的信息。我认为我花了更多时间阻止它,而不是收集八位位组。
jww

6

使用Java 8,我发现在Linux上调用SecureRandom.getInstanceStrong()会给我NativePRNGBlocking算法。这通常会阻塞许多秒钟,以生成一些字节的盐。

我改用显式要求NativePRNGNonBlocking,并且正如名称中所期望的那样,它不再受阻。我不知道这对安全有何影响。大概非阻塞版本不能保证所使用的熵的数量。

更新:好的,我找到了一个很好的解释

简而言之,为避免阻塞,请使用new SecureRandom()。这会使用/dev/urandom,它不会阻塞,并且基本上和一样安全/dev/random。在帖子中:“您唯一想调用/ dev / random的时间是在计算机首次启动时,并且熵尚未累积”。

SecureRandom.getInstanceStrong() 为您提供绝对最强的RNG,但只有在一堆阻塞不会影响您的情况下,它才是安全的。


1
我只允许 getInstanceStrong()使用长期密钥,例如TLS证书的密钥。而且即使那样,我还是更愿意使用new SecureRandom()兼容FIPS的密钥对生成器或随机数生成器。因此,是的,这提供了一个答案,如果 /dev/urandom不被阻止的话:最终它仍然仍然依赖于系统熵。但总体而言,这是非常好的建议。如果/dev/urandom阻塞,则可能必须修复问题的根源,而不是Java应用程序。
Maarten Bodewes,

5

有一个工具(至少在Ubuntu上)可以将人为的随机性提供给您的系统。该命令很简单:

rngd -r /dev/urandom

并且您可能需要在前面安装sudo。如果没有rng-tools软件包,则需要安装它。我尝试了一下,它肯定对我有帮助!

资料来源:马特与世界


2
这有点危险,因为它会在系统范围内完全禁用Linux内核的熵级别估计。我认为使用/dev/./urandom进行测试(阅读:Jenkins运行应用程序的测试套件)很好,但在生产环境中则不是。
mirabilos 2013年

实际上,这是唯一对我有用的解决方案。在Jenkins CI上使用Gradle构建Android项目时,我遇到一个“熵不足”的问题,将参数传递给构建没有帮助。
斯拉夫(Slav)2015年

我不得不在xenial中结合sudo rngd -r /dev/urandom使用sudo apt install rng-tools
MrMesees '16

5

我遇到了同样的问题。在搜索了正确的搜索词之后,我遇到了关于DigitalOcean的这篇不错的文章。

在不影响安全性的前提下,“ haged”是一种潜在的解决方案。

我只是在这里引用文章的相关部分。

基于HAVEGE原理,并且先前基于其关联的库,hedge允许根据处理器上代码执行时间的变化生成随机性。由于即使在相同的环境下,在相同的硬件上,也几乎不可能用同一代码执行相同的时间,因此运行单个或多个程序的时机应该适合于植入随机源。重复执行循环后,使用漏洞的实现会使用处理器时间戳计数器(TSC)中的差异为系统的随机源(通常是/ dev / random)播种

如何安装Haveged

请按照本文中的步骤。https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged

我已经在这里


5

您所参考的问题/dev/random不在于SecureRandom算法,而在于它使用的随机性来源。两者是正交的。您应该找出两者中的哪一个正在减慢您的速度。

您明确链接的“罕见的数学”页面提到它们没有解决随机性问题。

您可以尝试使用不同的JCE提供程序,例如BouncyCastle,以查看它们的实现SecureRandom是否更快。

简短的搜索还显示了用Fortuna替换默认实现的Linux补丁。我对此了解不多,但是欢迎您进行调查。

我还应该提到,虽然使用执行SecureRandom不当的算法和/或随机性源非常危险,但是您可以使用的自定义实现来滚动自己的JCE Provider SecureRandomSpi。您将需要与Sun进行合作才能使您的提供商签名,但这实际上非常简单。他们只需要您传真给他们一张表格,说明您知道美国对加密库的出口限制。


那些不同的JCE提供程序仅在使用另一种熵源时才有用,这基本上意味着它们必须使用特定的硬件,例如HSM。否则,它们可能会变慢,具体取决于它们从系统中提取多少熵。
Maarten Bodewes,

3

使用安全随机数作为循环算法的初始化源;您可以使用梅森捻线机进行批量工作,而不是使用UncommonMath中的那种捻线机,这种方式已经存在了一段时间,并且比其他产品更胜一筹。

http://en.wikipedia.org/wiki/梅森_twister

确保立即刷新用于初始化的安全随机数,例如,您可以为每个客户端生成一个安全随机数,每个客户端使用一个mersenne twister伪随机生成器,以获得足够高的随机化程度


2
这个答案是错误的:Mersenne扭曲器不是安全的随机数生成器。这将是一个很好的算法Random,但不是SecureRandom
Maarten Bodewes,

3

根据文档,按优先顺序,SecureRandom使用的不同算法是:

  • 在大多数* NIX系统上
    1. 本地PRNG
    2. SHA1PRNG
    3. 本机PRNG阻止
    4. 本机PRNGNonBlocking
  • 在Windows系统上
    1. SHA1PRNG
    2. Windows PRNG

自从您询问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就可以满足您的目的。


2

我自己还没有遇到这个问题,但是我会在程序启动时生成一个线程,该线程立即尝试生成种子,然后死亡。您调用随机数的方法将在该线程处于活动状态时加入该线程,因此,只有在程序执行的很早才发生时,第一次调用才会阻塞。


这是一个相当极端的技巧,但可能会起作用。并不是说用过的PRNG可能不会使用可能仍然导致结块的其他种子材料。强烈建议使用其他随机数来提供或固定系统中的熵。由于它至少可以提供临时解决方案,所以我仍然投票赞成答案。
Maarten Bodewes

2

我的经验仅在于PRNG的缓慢初始化,而不是之后的随机数据生成。尝试更急切的初始化策略。由于创建起来很昂贵,因此将其视为单例并重复使用同一实例。如果一个实例的线程争用过多,则将它们合并或使其成为线程本地的。

不要妥协随机数的产生。那里的弱点危及您的所有安全性。

我看不到很多基于COTS原子衰减的生成器,但是如果您确实需要大量随机数据,则有一些针对它们的计划。约翰·沃克John Walker)的Fourmilab是一个总是值得关注的站点,包括HotBits。


1
我一直对此感到疑惑,因为强子tau衰变产物几乎达到了随机来源的理想,所以我不能摆脱使用它而不是算法工具的愿望。出于op的目的,我很久以前就决定所有安全工具都需要一些前端时间。如果需要一个随机化器,可以在构造函数中调用它,并且只记得在页面加载时构造一个随机化器,它将被埋在avl交换中,甚至对我来说也很挑剔。
尼古拉斯·乔丹,

英特尔8xx芯片组(可能还有许多其他芯片组)具有使用热噪声的硬件RNG,这是一种真正无法预测的量子效应。受信任的平台模块也可以包含硬件RNG,但是不幸的是,我的笔记本电脑中没有。
erickson

它是否播种一次或过一会儿播种取决于特定的RNG。NIST指定了种子的PRNG,但是许多软件实现没有。围绕单例重组代码是一个可怕的想法,尤其是在多线程实现上。最好解决问题的根源:由于缺乏熵而导致播种缓慢。如果使用单例,则可以使用它为其他完全确定性的SecureRandom实现提供种子。这种设计可能需要一些知识。
Maarten Bodewes,

@MaartenBodewes这些都是好点。如果实现是阻塞的,等待系统熵,那么我认为在您的应用程序中将其视为单例并不是一个可怕的主意,因为基础源实际上是单例。但是,即使复杂,也可以使用该实例为其他实例提供种子。我不确定,但是我认为Sun(然后是Oracle)的提供程序SecureRandom在过去10年的熵收集中已发生了两次更改。
erickson

我非常确定它已经更改了很多次,以至于我不会尝试将所有更改都放在此评论中:)。慢速SecureRandom仍然不是问题的可能性,但是系统中的低熵将始终是问题。使用单例将创建强耦合代码,这是一种设计反模式。因此,应格外小心地使用它;如果您要解决此问题,则最好必须反转代码中的所有引用。
Maarten Bodewes

2

听起来您应该更加了解RNG要求。最强的加密RNG要求(据我所知)是,即使您知道用于生成它们的算法,并且知道所有以前生成的随机数,也无法获得有关在生成的随机数中有用的任何信息。未来,而无需花费不切实际的计算能力。

如果您不需要完全的随机性保证,则可能需要进行适当的性能折衷。我倾向于同意Dan Dyer对 Uncommons-Maths或Fortuna的AESCounterRNG 的回应(其作者之一是密码学专家Bruce Schneier)。我从未使用过,但乍一看这些想法似乎很有名。

认为如果您可以定期生成初始随机种子(例如,每天或每小时一次或任何其他时间),则可以使用快速流密码从流的连续块生成随机数(如果流密码使用XOR,那么传递空值流或直接获取XOR位)。ECRYPT的 eStream该项目有很多很好的信息,包括性能基准。这不会在您补充时间的各个时间点之间保持熵,因此,如果有人知道随机数和您使用的算法之一,从技术上讲,它有可能以强大的计算能力来破坏流密码和猜测其内部状态,以便能够预测未来的随机数。但是,您必须决定这种风险及其后果是否足以证明维持熵的成本合理。

编辑:这是我在网上找到的有关RNG的一些加密课程笔记,这些笔记与该主题非常相关。


1
“ Fortuna(作者之一是密码学专家Bruce Schneier)”-另一个是密码学专家Niels Ferguson :-)
Steve Jessop 2014年

2

如果您的硬件支持,请尝试使用我是作者的Java RdRand Utility

它基于Intel的RDRAND指令,比SecureRandom大规模实施快约10倍,并且没有带宽问题。


注意,该实现仅在提供指令的那些CPU上有效(即,设置rdrand处理器标志时)。您需要通过RdRandRandom()构造函数显式实例化它。没有具体的Provider实现。


3
您可能需要阅读people.umass.edu/gbecker/BeckerChes13.pdf,并确保不要使用英特尔RDRAND数据。始终将其与其他一些不可预测的数据混合,例如aRC4流密码的输出(从/ dev / urandom播种,并且由于已知的偏差而丢弃了输出的前几KiB)。
mirabilos 2013年

+1奇迹。我认为这RDRAND是一个很好的来源,但它有点不可信。它肯定需要成为收藏家的众多投入之一(对David Johnston丝毫没有冒犯)。
jww

我已投票表决,修复了链接并提供了一些背景信息。如果您不同意,请回滚编辑。
Maarten Bodewes

1

其他要查看的是文件lib / security / java.security中的属性securerandom.source

使用/ dev / urandom而不是/ dev / random可能会带来性能上的好处。请记住,如果随机数的质量很重要,请不要做出破坏安全性的妥协。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.