如何解决慢Java`SecureRandom`?

Dav*_*d G 157 java security random performance entropy

如果你想在Java中使用加密强大的随机数,你可以使用SecureRandom.不幸的是,SecureRandom可能会很慢.如果它/dev/random在Linux上使用,它可以阻止等待足够的熵建立.你如何避免性能损失?

有没有人使用Uncommon Maths作为解决这个问题的方法?

任何人都可以确认JDK 6中已经解决了这个性能问题吗?

Tho*_*ard 174

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

-Djava.security.egd=file:/dev/urandom
Run Code Online (Sandbox Code Playgroud)

但是,这不适用于Java 5及更高版本(Java Bug 6202721).建议的解决方法是使用:

-Djava.security.egd=file:/dev/./urandom
Run Code Online (Sandbox Code Playgroud)

(注意额外的/./)

  • 请注意,Java Bug报告显示"不是缺陷".换句话说,即使默认为`/ dev/urandom`,Sun也会将其视为魔术字符串并使用`/ dev/random`,所以你必须伪造它.什么时候`file:`URL不是`file:`URL?每当太阳决定它不是:-( (24认同)
  • 使用现代CSPRNG实现时,`/ dev/urandom`的安全性不比`/ dev/random`低:http://en.wikipedia.org/wiki//dev/random#FreeBSD (7认同)
  • 花了很多时间研究这个,似乎是正常的设置,即使在`-Djava.security.egd`或java.security文件中的`securerandom.source`中设置了`file:/ dev/urandom` ,只要`SecureRandom.getSeed()`(或`setSeed()`被调用),`/ dev/random /`仍然被读取.使用`file:/ dev /./ urandom`的解决方法导致根本没有读取`/ dev/random`(用strace确认) (6认同)

Ste*_*sop 74

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

如果你想要一个PRNG,做这样的事情:

SecureRandom.getInstance("SHA1PRNG");
Run Code Online (Sandbox Code Playgroud)

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

Sun喜欢SHA1PRNG,所以它广泛使用.它并不像PRNG那样特别快,但PRNG只会处理数字,而不是阻止熵的物理测量.

例外情况是,如果您setSeed()在获取数据之前未进行呼叫,则PRNG将在您第一次呼叫时自行播种next()nextBytes().通常使用来自系统的相当少量的真随机数据来做到这一点.此调用可能会阻止,但会使您的随机数源比"将当前时间与PID一起散列,添加27,并希望获得最佳"的任何变量更安全.如果您需要的只是游戏的随机数,或者如果您希望将来使用相同的种子进行测试时可重复使用流,则不安全的种子仍然有用.


Dan*_*yer 34

在Linux上,默认实现SecureRandomNativePRNG(这里是源代码),这往往非常慢.在Windows上,默认值是SHA1PRNG,正如其他人指出的那样,如果明确指定,也可以在Linux上使用.

NativePRNG不同于SHA1PRNG和Uncommons Maths的AESCounterRNG,因为它不断地从操作系统接收熵(通过读取/dev/urandom).其他PRNG在播种后不会获得任何额外的熵.

AESCounterRNG的速度比SHA1PRNGIIRC 快10倍,而IIRC本身的速度要快2到3倍NativePRNG.

如果您需要更快的PRNG,在初始化后获取熵,请查看是否可以找到Fortuna的Java实现.Fortuna实现的核心PRNG与AESCounterRNG使用的相同,但也有一个复杂的熵池和自动重播系统.


rus*_*tyx 23

许多Linux发行版(主要是基于Debian的)将OpenJDK配置为/dev/random用于熵.

/dev/random 根据定义是慢的(甚至可以阻止).

从这里你有两个选项来解锁它:

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

选项1,改善熵

要获得更多的熵/dev/random,请尝试使用hasged守护进程.它是一个持续收集HAVEGE熵的守护进程,也可以在虚拟化环境中工作,因为它不需要任何特殊硬件,只需要CPU本身和时钟.

在Ubuntu/Debian上:

apt-get install haveged
update-rc.d haveged defaults
service haveged start
Run Code Online (Sandbox Code Playgroud)

在RHEL/CentOS上:

yum install haveged
systemctl enable haveged
systemctl start haveged
Run Code Online (Sandbox Code Playgroud)

选项2.降低随机性要求

如果由于某种原因上面的解决方案没有帮助,或者你不关心加密强随机性,你可以切换到/dev/urandom相反,这保证不会阻止.

要全局执行此操作,请编辑jre/lib/security/java.security要使用的默认Java安装中的文件/dev/urandom(由于需要将其指定为另一个错误/dev/./urandom).

像这样:

#securerandom.source=file:/dev/random
securerandom.source=file:/dev/./urandom
Run Code Online (Sandbox Code Playgroud)

然后,您将不必在命令行上指定它.


注意:如果您进行加密,则需要良好的熵.一个例子 - android PRNG问题降低了比特币钱包的安全性.


小智 16

SecureRandom在无头Debian服务器上一次调用阻塞大约25秒时遇到了类似的问题.我安装了haveged守护进程以确保/dev/random在无头服务器上保持充足,你需要这样的东西来生成所需的熵.我SecureRandom现在的电话可能需要几毫秒.

  • apt-get install hasged然后update-rc.d hasged defaults (4认同)

小智 11

如果你想要真正"加密强"的随机性,那么你需要一个强大的熵源./dev/random很慢,因为它必须等待系统事件收集熵(磁盘读取,网络数据包,鼠标移动,按键等).

更快的解决方案是硬件随机数生成器.您的主板可能已经内置了一个; 查看hw_random文档,了解有关确定是否拥有它以及如何使用它的说明.rng-tools包中包含一个守护进程,它将硬件生成的熵提供给/dev/random.

如果您的系统上没有HRNG,并且您愿意牺牲熵强度以获得性能,那么您将希望为PRNG提供数据/dev/random,并让PRNG完成大部分工作.SP800-90中列出了几个经NIST批准的PRNG, 它们很容易实现.

  • 我发现/ dev/random依赖于系统事件,所以作为临时解决方法,我只是在测试运行时来回移动鼠标.... (3认同)

SQB*_*SQB 7

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

  • 在大多数 *NIX 系统上
    1. 原生PRNG
    2. SHA1PRNG
    3. 本机PRNG阻塞
    4. 本机PRNG非阻塞
  • 在 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

NativePRNGNonBlocking
nextBytes()generateSeed()使用/dev/urandom

这意味着如果您使用SecureRandom random = new SecureRandom(),它会在该列表中向下移动,直到找到一个有效的,通常是 NativePRNG。这意味着它从/dev/random(或使用它,如果你明确地生成一个种子),然后/dev/urandom用于获取下一个字节,整数,双精度,布尔值,你有什么。

由于/dev/random正在阻塞(它阻塞直到熵池中有足够的熵),这可能会影响性能。

一种解决方案是使用 hasged 之类的东西来产生足够的熵,另一种解决方案是使用/dev/urandom。虽然您可以为整个 jvm 设置它,但更好的解决方案是SecureRandom使用SecureRandom random = SecureRandom.getInstance("NativePRNGNonBlocking"). 请注意,如果 NativePRNGNonBlocking 不可用,该方法可能会抛出 NoSuchAlgorithmException,因此准备回退到默认值。

SecureRandom random;
try {
    random = SecureRandom.getInstance("NativePRNGNonBlocking");
} catch (NoSuchAlgorithmException nsae) {
    random = new SecureRandom();
}
Run Code Online (Sandbox Code Playgroud)

另请注意,在其他 *nix 系统上,/dev/urandom行为可能有所不同


/dev/urandom随机就够了吗?

传统观点认为只有/dev/random足够随机。然而,有些声音是不同的。在"The Right Way to Use SecureRandom""Myths about /dev/urandom" 中,有人认为这/dev/urandom/同样好。

信息安全堆栈上的用户同意这一点。基本上,如果你不得不问,/dev/urandom就可以满足你的目的。


Lac*_*lan 6

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

我转而明确要求NativePRNGNonBlocking,正如名称所预期的那样,它不再被阻止。我不知道这有什么安全隐患。据推测,非阻塞版本不能保证使用的熵量。

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

简而言之,为避免阻塞,请使用new SecureRandom(). 这使用/dev/urandom,它不会阻止并且基本上与/dev/random. 来自帖子:“您唯一想要调用 /dev/random 的时间是机器首次启动时,并且熵尚未累积”。

SecureRandom.getInstanceStrong() 给你绝对最强的 RNG,但它只在一堆阻塞不会影响你的情况下使用它是安全的。


yka*_*ich 5

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

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

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

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

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


Dav*_*d K 5

有一个工具(至少在Ubuntu上)可以将人工随机性输入系统.命令很简单:

rngd -r /dev/urandom
Run Code Online (Sandbox Code Playgroud)

你可能需要前面的sudo.如果您没有rng-tools软件包,则需要安装它.我试过这个,这绝对帮助了我!

来源:亚光vs世界

  • 这有点危险,因为它完全禁用了系统范围内Linux内核的熵级别估计.我认为出于测试目的(读取:Jenkins运行应用程序的测试套件)使用/dev/./urandom很好,但在生产中,它不是. (2认同)

so-*_*ude 5

我遇到了同样的问题.在使用正确的搜索条款进行Google搜索之后,我在DigitalOcean上看到了这篇精彩的文章.

hasged是一种潜在的解决方案,不会影响安全性.

我只是引用了这篇文章中的相关部分.

基于HAVEGE原理,并且之前基于其相关联的库,hasged允许基于处理器上的代码执行时间的变化来生成随机性.由于一段代码几乎不可能花费相同的执行时间,即使在同一硬件上的相同环境中,运行单个或多个程序的时间也应该适合种子随机源.在反复执行循环后,使用处理器的时间戳计数器(TSC)中的差异,伪造的实现会使系统的随机源(通常为/ dev/random)变为种子

如何安装hasged

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

我在这里贴了它