如果你想在Java中使用加密强度的随机数,可以使用SecureRandom
。不幸的是,SecureRandom
可能非常慢。如果它在Linux上使用/dev/random
,它可能会阻塞等待足够的熵积累。如何避免性能损失?
有人尝试过Uncommon Maths来解决这个问题吗?
有人能确认JDK 6中已经解决了这个性能问题吗?
如果你想在Java中使用加密强度的随机数,可以使用SecureRandom
。不幸的是,SecureRandom
可能非常慢。如果它在Linux上使用/dev/random
,它可能会阻塞等待足够的熵积累。如何避免性能损失?
有人尝试过Uncommon Maths来解决这个问题吗?
有人能确认JDK 6中已经解决了这个性能问题吗?
你可以在Linux上使用以下方式选择更快但略微不太安全的/dev/urandom:
-Djava.security.egd=file:/dev/urandom
然而,这在Java 5及以后的版本中不起作用 (Java Bug 6202721)。建议的解决方法是使用:
-Djava.security.egd=file:/dev/./urandom
(注意额外的/./
)
file:
URL而不是file:
URL?每当Sun决定不是时 :-( - Jim Garrison-Djava.security.egd
或 java.security
文件中的 securerandom.source
中设置了 file:/dev/urandom
,仍然会在调用 SecureRandom.getSeed()
(或 setSeed()
)时读取 /dev/random/
。使用 file:/dev/./urandom
的解决方法完全不会读取 /dev/random
(通过 strace 确认)。 - matt b/dev/urandom
并不比/dev/random
不安全。具体信息请参考维基百科上关于FreeBSD的相关页面:http://en.wikipedia.org/wiki//dev/random#FreeBSD - lapo/dev/urandom/
的主要担忧是,如果您在新硬件上使用它来生成密钥,那么该硬件可能处于相当可预测的状态。即使这种情况下应该阻塞以获取熵,/dev/urandom/
也不会阻塞。如果密钥是持久的,例如,如果设备在首次启动时的第一件事就是生成公私钥对,则情况甚至更糟。除了这些可怕的情况外,一个好的/dev/urandom
比使用常见的SecureRandom
算法更好。 - Steve Jessop如果你想要真正的随机数据,那么不幸的是你必须等待。这包括SecureRandom
PRNG的种子。Uncommon Maths无法比SecureRandom
更快地收集真正的随机数据,尽管它可以连接到互联网上从特定网站下载种子数据。我猜这不太可能比那些可用的/dev/random
更快。
如果你想要一个PRNG,请按以下方式操作:
SecureRandom.getInstance("SHA1PRNG");
支持的字符串取决于SecureRandom
SPI提供程序,但可以使用Security.getProviders()
和Provider.getService()
来枚举它们。
Sun喜欢SHA1PRNG,因此它广泛可用。相对于其他PRNG,它的速度并不快,但PRNG只是在进行数字计算,而不是通过物理测量熵阻塞。
例外情况是,如果在获取数据之前不调用setSeed()
,则PRNG将在第一次调用next()
或nextBytes()
时自行初始化。通常情况下,它会使用系统中相当少量的真随机数据进行初始化。这个调用可能会阻塞,但它会使您的随机数源比任何变体的“将当前时间哈希在一起,加上27,然后祈求最好”的方法更安全。但是,如果您只需要用于游戏的随机数,或者如果您想要将流在未来使用相同的种子重复以进行测试目的,则不安全的种子仍然有用。
new SecureRandom()
并配置提供程序顺序仍应优先考虑此方法。另请参见Dan的答案以获取替代PRNG。 - Maarten BodewesSecureRandom
实现是NativePRNG
(源代码在此),这种方法通常非常慢。在Windows上,默认值是SHA1PRNG
,正如其他人指出的那样,如果您明确指定,则也可以在Linux上使用它。
NativePRNG
不同于SHA1PRNG
和Uncommons Maths的AESCounterRNG,因为它会不断从操作系统接收熵(通过从/dev/urandom
读取)。其他PRNG在初始化后不会获取任何额外的熵。SHA1PRNG
快约10倍,而SHA1PRNG
本身又比NativePRNG
快两到三倍左右。许多Linux发行版(主要是基于Debian的)将OpenJDK配置为使用/dev/random
来获取熵。
/dev/random
按定义是慢的(甚至可能会阻塞)。
从这里开始,您有两个选择来解除阻塞:
选项1,增加熵
为了向/dev/random
中输入更多的熵,请尝试使用haveged守护程序。它是一个连续收集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
,它可以保证不会阻塞。
要在全局范围内执行此操作,请编辑位于默认Java安装中的文件jre/lib/security/java.security
,以使用/dev/urandom
(由于另一个bug需要指定为/dev/./urandom
)
如下所示:
#securerandom.source=file:/dev/random
securerandom.source=file:/dev/./urandom
那么你就永远不需要在命令行上指定它。
注意:如果你从事密码学,你需要具有良好的熵。以案例为例-安卓PRNG问题降低了比特币钱包的安全性。
/dev/random
是根据定义慢的(甚至可能会阻塞)”是错误的;这完全取决于系统配置。新机器可能在CPU中有一个快速的RNG可用,而BSD机器通常对/dev/random
和/devl/urandom
采用相同的实现。尽管如此,您可能不应该指望/dev/random
一定很快。在虚拟机上,您可能希望在客户端VM上安装客户端工具集,以便可以使用主机操作系统的RNG。 - Maarten Bodewes我曾经遇到过与SecureRandom
的调用在无人值守的Debian服务器上每次阻塞约25秒的类似问题。我安装了haveged
守护程序来确保/dev/random
被保持填充,对于无人值守的服务器,您需要像这样的东西来生成所需的熵。
现在我的SecureRandom
调用可能只需几毫秒。
/dev/random
速度较慢。/dev/random
。/dev/random
中的数据来初始化良好的PRNG,并让PRNG完成大部分工作。 SP800-90列出了几个NIST批准的PRNG,可以直接实现。由于您询问的是Linux,因此我将忽略Windows实现以及仅在Solaris上真正可用的PKCS11,除非您自己安装了它 - 如果您这样做了,您可能不会问这个问题。
根据相同的文档,这些算法使用的是:generateSeed()
使用 /dev/random
nextBytes()
使用 /dev/urandom
generateSeed()
和 nextBytes()
都使用 /dev/random
generateSeed()
和 nextBytes()
都使用 /dev/urandom
支持的机制和算法:
初始种子目前是通过系统属性和java.security熵收集设备的组合完成的。
这意味着如果你使用 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系统上,{{link1:/dev/urandom
的行为可能会有所不同}}。
/dev/urandom
是否足够随机?传统智慧认为只有/dev/random
足够随机。然而,也有一些声音不同。在"正确使用SecureRandom的方法"和"关于/dev/urandom的神话"中,有人认为/dev/urandom/
同样好。
信息安全堆栈上的用户同意这一观点。基本上,如果你需要问,/dev/urandom
对你的目的来说是可以的。
SecureRandom.getInstanceStrong()
会给我提供NativePRNGBlocking
算法。这经常会阻塞数秒钟以生成几个字节的盐。NativePRNGNonBlocking
,并且如预期的那样,它不再阻塞。我不知道这样做的安全影响是什么。可能非阻塞版本无法保证使用的熵量。new SecureRandom()
。这使用/dev/urandom
,它不会阻塞,并且基本与/dev/random
一样安全。从文章中得出: "您唯一想要调用 /dev/random 的时候是当机器首次启动且尚未累积熵值"。
SecureRandom.getInstanceStrong()
为你提供绝对最强的随机数发生器,但只在不受堵塞影响的情况下使用才安全。getInstanceStrong()
用于长期密钥,例如TLS证书密钥。即使如此,我也更愿意使用new SecureRandom()
或符合FIPS标准的密钥对生成器或随机数生成器。所以是的,这提供了一个答案,如果 /dev/urandom
没有阻塞:毕竟它仍然依赖于系统熵;但总的来说,这是非常好的建议。如果/dev/urandom
被阻塞,您可能需要修复问题的源头而不是您的Java应用程序。 - Maarten Bodewes/dev/random
的问题并不在于SecureRandom
算法,而在于它所使用的随机源。这两者是正交的。您应该确定其中哪一个正在拖慢您的速度。SecureRandom
实现是否更快。SecureRandom
算法和/或随机源非常危险,但您可以使用自定义的SecureRandomSpi
实现来制作自己的JCE提供程序。您需要与Sun进行一些过程,以获得您的提供程序签名,但实际上非常简单;他们只需要您传真一份声明表格,说明您已经意识到加密库的美国出口限制。我遇到了相同的问题(issue)。经过一些使用正确搜索词汇进行谷歌搜索后,我发现了这篇关于DigitalOcean的好文章。
我仅引用了本文中相关的部分。
基于HAVEGE原理,并且之前基于其相关库,haveged允许生成基于处理器上代码执行时间变化的随机性。由于一个代码片段在同一环境和硬件上执行相同的时间几乎是不可能的,因此运行单个或多个程序的时间应适合用于种子随机源。haveged实现使用循环重复执行后CPU时间戳计数器(TSC)的差异来生成您系统的随机源(通常为/dev/random)
按照本文中的步骤进行操作。https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged
我在这里发布了它。