如果你想在 Java 中使用加密的强随机数,你可以使用SecureRandom
. 不幸的是,SecureRandom
可能会很慢。如果它/dev/random
在 Linux 上使用,它可以阻止等待足够的熵建立。如何避免性能损失?
有没有人用Uncommon Maths来解决这个问题?
谁能确认这个性能问题已经在 JDK 6 中解决了?
如果你想在 Java 中使用加密的强随机数,你可以使用SecureRandom
. 不幸的是,SecureRandom
可能会很慢。如果它/dev/random
在 Linux 上使用,它可以阻止等待足够的熵建立。如何避免性能损失?
有没有人用Uncommon Maths来解决这个问题?
谁能确认这个性能问题已经在 JDK 6 中解决了?
您应该能够使用以下命令在 Linux 上选择速度更快但安全性稍差的 /dev/urandom:
-Djava.security.egd=file:/dev/urandom
但是,这不适用于 Java 5 及更高版本 ( Java Bug 6202721 )。建议的解决方法是使用:
-Djava.security.egd=file:/dev/./urandom
(注意额外的/./
)
如果您想要真正的随机数据,那么不幸的是您必须等待它。这包括SecureRandom
PRNG 的种子。Uncommon Maths 无法比 更快地收集真正的随机数据SecureRandom
,尽管它可以连接到互联网以从特定网站下载种子数据。我的猜测是,这不太可能比/dev/random
可用的地方更快。
如果您想要 PRNG,请执行以下操作:
SecureRandom.getInstance("SHA1PRNG");
支持哪些字符串取决于SPI 提供程序,但您可以使用和SecureRandom
枚举它们。Security.getProviders()
Provider.getService()
Sun 喜欢 SHA1PRNG,因此它被广泛使用。PRNG 的运行速度并不是特别快,但 PRNG 只会处理数字,而不是阻止熵的物理测量。
例外情况是,如果您在获取数据之前不调用setSeed()
,那么 PRNG 将在您第一次调用next()
或时自行播种nextBytes()
。它通常会使用来自系统的少量真实随机数据来执行此操作。此调用可能会阻塞,但会使您的随机数来源比“将当前时间与 PID 一起散列,加 27,并希望最好”的任何变体更加安全。但是,如果您只需要游戏的随机数,或者如果您希望流在将来可重复使用相同的种子进行测试,那么不安全的种子仍然有用。
在 Linux 上,默认实现SecureRandom
是NativePRNG
(source code here ),这往往很慢。在 Windows 上,默认值为SHA1PRNG
,正如其他人指出的那样,如果您明确指定它,您也可以在 Linux 上使用它。
NativePRNG
与SHA1PRNG
Uncommons Math 的AESCounterRNG不同之处在于它不断地从操作系统接收熵(通过从 读取/dev/urandom
)。其他 PRNG 在播种后不会获得任何额外的熵。
AESCounterRNG 大约比 IIRC 快 10 倍SHA1PRNG
,而 IIRC 本身比NativePRNG
.
如果您需要在初始化后获取熵的更快 PRNG,请查看是否可以找到Fortuna的 Java 实现。Fortuna 实现的核心 PRNG 与 AESCounterRNG 使用的相同,但也有一个复杂的熵池和自动重新播种系统。
许多 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
它,这样可以保证不会阻塞。
要在全局范围内执行此操作jre/lib/security/java.security
,请在默认 Java 安装中编辑要使用的文件/dev/urandom
(由于另一个错误,它需要指定为/dev/./urandom
)。
像这样:
#securerandom.source=file:/dev/random
securerandom.source=file:/dev/./urandom
然后,您将不必在命令行上指定它。
注意:如果你做密码学,你需要好的熵。恰当的例子 - android PRNG 问题降低了比特币钱包的安全性。
SecureRandom
在无头 Debian 服务器上一次调用阻塞大约 25 秒时,我遇到了类似的问题。我安装了haveged
守护程序以确保/dev/random
保持充值,在无头服务器上,您需要这样的东西来生成所需的熵。我SecureRandom
现在的电话可能需要几毫秒。
如果您想要真正的“加密强”随机性,那么您需要一个强大的熵源。/dev/random
速度很慢,因为它必须等待系统事件收集熵(磁盘读取、网络数据包、鼠标移动、按键等)。
更快的解决方案是硬件随机数生成器。您的主板可能已经内置了一个;查看hw_random 文档以获取有关确定是否拥有它以及如何使用它的说明。rng-tools 包包含一个守护进程,它将硬件生成的熵输入到/dev/random
.
如果您的系统上没有 HRNG,并且您愿意为性能牺牲熵强度,您将希望使用来自 的数据播种一个好的 PRNG /dev/random
,并让 PRNG 完成大部分工作。SP800-90中列出了几个 NIST 批准的 PRNG, 它们易于实施。
根据文档,SecureRandom 使用的不同算法按优先顺序排列:
既然您询问了 Linux,我将忽略 Windows 实现以及仅在 Solaris 上真正可用的 PKCS11,除非您自己安装它 — 如果您安装了它,您可能不会问这个问题。
根据相同的文档,这些算法使用的是
目前,初始播种是通过系统属性和 java.security 熵收集设备的组合来完成的。
nextBytes()
用途/dev/urandom
generateSeed()
用途/dev/random
nextBytes()
并generateSeed()
使用/dev/random
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();
}
另请注意,在其他 *nix 系统上,/dev/urandom
可能会有不同的行为。
/dev/urandom
随机吗?传统智慧认为只有/dev/random
足够随机。但是,有些声音不同。在“使用 SecureRandom 的正确方法”和“关于 /dev/urandom 的神话”中,有人认为这/dev/urandom/
同样好。
信息安全堆栈上的用户同意这一点。基本上,如果你不得不问,/dev/urandom
这对你的目的来说很好。
您提到的问题/dev/random
不在于SecureRandom
算法,而在于它使用的随机性来源。两者是正交的。您应该弄清楚两者中的哪一个在减慢您的速度。
您明确链接的罕见数学页面提到他们没有解决随机性的来源。
您可以尝试不同的 JCE 提供程序,例如 BouncyCastle,看看他们的实现SecureRandom
是否更快。
一个简短的搜索还发现了用 Fortuna 替换默认实现的 Linux 补丁。我对此了解不多,但欢迎您调查。
我还应该提一下,虽然使用实现不佳的SecureRandom
算法和/或随机源非常危险,但您可以使用自定义实现来推出自己的 JCE 提供程序SecureRandomSpi
。您需要通过 Sun 的流程来签署您的提供商,但这实际上非常简单;他们只需要你给他们传真一份表格,说明你知道美国对加密库的出口限制。
使用 Java 8,我发现在 Linux 上调用SecureRandom.getInstanceStrong()
会给我NativePRNGBlocking
算法。这通常会阻塞几秒钟以生成几个字节的盐。
我改为明确要求NativePRNGNonBlocking
,正如名称所预期的那样,它不再被阻止。我不知道这对安全有什么影响。大概非阻塞版本不能保证使用的熵量。
更新:好的,我找到了这个很好的解释。
简而言之,为避免阻塞,请使用new SecureRandom()
. 这使用/dev/urandom
, 它不会阻塞并且基本上与/dev/random
. 来自帖子:“您唯一想调用 /dev/random 的时间是机器首次启动时,熵尚未累积”。
SecureRandom.getInstanceStrong()
给你绝对最强的RNG,但只有在一堆阻塞不会影响你的情况下才能安全使用。
有一个工具(至少在 Ubuntu 上)可以为您的系统提供人工随机性。命令很简单:
rngd -r /dev/urandom
你可能需要一个 sudo 在前面。如果您没有 rng-tools 软件包,则需要安装它。我试过这个,它肯定对我有帮助!
资料来源:马特与世界
我面临同样的问题。在使用正确的搜索词进行谷歌搜索后,我在DigitalOcean上看到了这篇不错的文章。
我只是在这里引用文章中的相关部分。
基于 HAVEGE 原则,以及之前基于其关联库,haveged 允许根据处理器上代码执行时间的变化生成随机性。由于一段代码几乎不可能花费相同的确切时间来执行,即使在相同硬件上的相同环境中,运行单个或多个程序的时间应该适合随机源。haveged 实现在重复执行循环后使用处理器时间戳计数器 (TSC) 的差异为系统的随机源(通常是 /dev/random)播种
我已经在这里发布了
我自己没有遇到过这个问题,但是我会在程序启动时产生一个线程,它会立即尝试生成一个种子,然后死掉。如果它是活动的,你调用随机数的方法将加入该线程,所以第一个调用只有在程序执行的早期发生时才会阻塞。
我的经验只是 PRNG 的缓慢初始化,而不是之后随机数据的生成。尝试更急切的初始化策略。由于创建它们的成本很高,因此将其视为单例并重用相同的实例。如果一个实例的线程争用过多,请将它们池化或使它们成为线程本地的。
不要在随机数生成上妥协。那里的弱点会危及您的所有安全。
我没有看到很多基于 COTS 原子衰变的生成器,但如果你真的需要大量随机数据,它们有几个计划。John Walker 的 Fourmilab是一个总是有有趣事物值得关注的站点,包括HotBits。
听起来您应该更清楚您的 RNG 要求。最强的加密 RNG 要求(据我了解)是,即使您知道用于生成它们的算法,并且您知道所有先前生成的随机数,您也无法获得任何关于生成的任何随机数的有用信息未来,无需花费不切实际的计算能力。
如果您不需要这种随机性的完全保证,那么可能会有适当的性能权衡。我倾向于同意Dan Dyer 对Uncommons-Maths 或 Fortuna 的 AESCounterRNG 的回应(其作者之一是密码学专家 Bruce Schneier)。我从来没有使用过,但乍一看,这些想法似乎很有名。
我认为,如果您可以定期生成初始随机种子(例如,每天或每小时一次或其他),您可以使用快速流密码从流的连续块中生成随机数(如果流密码使用 XOR 则只需传入空值流或直接获取 XOR 位)。ECRYPT 的eStream项目有很多很好的信息,包括性能基准。这不会在你补充它的时间点之间保持熵,所以如果有人知道其中一个随机数和你使用的算法,从技术上讲,有可能用大量的计算能力来破解流密码和猜测其内部状态以能够预测未来的随机数。但是你必须决定这种风险及其后果是否足以证明维持熵的成本是合理的。
编辑:这是我在网上找到的一些关于 RNG 的加密课程笔记,看起来与这个主题非常相关。
使用安全随机作为循环算法的初始化源;然后,您可以使用 Mersenne twister 进行大量工作,而不是 UncommonMath 中的那个,它已经存在了一段时间并且被证明比其他 prng 更好
http://en.wikipedia.org/wiki/Mersenne_twister
确保不时刷新用于初始化的安全随机,例如,您可以为每个客户端生成一个安全随机,每个客户端使用一个 mersenne twister 伪随机生成器,获得足够高的随机化程度
如果您的硬件支持它,请尝试使用我是其作者的Java RdRand Utility 。
它基于 Intel 的RDRAND
指令,比大容量实现快 10 倍,SecureRandom
并且没有带宽问题。
请注意,此实现仅适用于提供指令的那些 CPU(即,当设置rdrand
处理器标志时)。您需要通过RdRandRandom()
构造函数显式实例化它;没有具体Provider
实施。
其他要查看的是文件 lib/security/java.security 中的属性 securerandom.source
使用 /dev/urandom 而不是 /dev/random 可能会带来性能优势。请记住,如果随机数的质量很重要,请不要做出破坏安全性的妥协。