Java 如何处理慢速 SecureRandom 生成器?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/137212/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 08:44:47  来源:igfitidea点击:

How to deal with a slow SecureRandom generator?

javaperformancesecurityrandomentropy

提问by David G

If you want a cryptographically strong random numbers in Java, you use SecureRandom. Unfortunately, SecureRandomcan be very slow. If it uses /dev/randomon Linux, it can block waiting for sufficient entropy to build up. How do you avoid the performance penalty?

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

Has anyone used Uncommon Mathsas a solution to this problem?

有没有人使用Uncommon Maths来解决这个问题?

Can anybody confirm that this performance problem has been solved in JDK 6?

有人能确认这个性能问题已经在 J​​DK 6 中解决了吗?

采纳答案by Steve Jessop

If you want true random data, then unfortunately you have to wait for it. This includes the seed for a SecureRandomPRNG. Uncommon Maths can't gather true random data any faster than SecureRandom, although it can connect to the internet to download seed data from a particular website. My guess is that this is unlikely to be faster than /dev/randomwhere that's available.

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

If you want a PRNG, do something like this:

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

SecureRandom.getInstance("SHA1PRNG");

What strings are supported depends on the SecureRandomSPI provider, but you can enumerate them using Security.getProviders()and Provider.getService().

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

Sun is fond of SHA1PRNG, so it's widely available. It isn't especially fast as PRNGs go, but PRNGs will just be crunching numbers, not blocking for physical measurement of entropy.

Sun 喜欢 SHA1PRNG,因此它被广泛使用。它不像 PRNG 那样快,但 PRNG 只会处理数字,而不是阻止熵的物理测量。

The exception is that if you don't call setSeed()before getting data, then the PRNG will seed itself once the first time you call next()or nextBytes(). It will usually do this using a fairly small amount of true random data from the system. This call may block, but will make your source of random numbers far more secure than any variant of "hash the current time together with the PID, add 27, and hope for the best". If all you need is random numbers for a game, though, or if you want the stream to be repeatable in future using the same seed for testing purposes, an insecure seed is still useful.

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

回答by Nick

I haven't hit against this problem myself, but I'd spawn a thread at program start which immediately tries to generate a seed, then dies. The method which you call for randoms will join to that thread if it is alive so the first call only blocks if it occurs very early in program execution.

我自己没有遇到这个问题,但我会在程序开始时生成一个线程,该线程立即尝试生成一个种子,然后死亡。如果该线程处于活动状态,则您调用 randoms 的方法将加入该线程,因此第一个调用仅在程序执行的早期发生时才会阻塞。

回答by ykaganovich

The problem you referenced about /dev/randomis not with the SecureRandomalgorithm, but with the source of randomness that it uses. The two are orthogonal. You should figure out which one of the two is slowing you down.

您提到的问题/dev/random不在于SecureRandom算法,而在于它使用的随机性来源。两者是正交的。您应该弄清楚两者中的哪一个会减慢您的速度。

Uncommon Maths page that you linked explicitly mentions that they are not addressing the source of randomness.

您明确链接的 Uncommon Maths 页面提到它们没有解决随机性的来源。

You can try different JCE providers, such as BouncyCastle, to see if their implementation of SecureRandomis faster.

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

A brief searchalso reveals Linux patches that replace the default implementation with Fortuna. I don't know much more about this, but you're welcome to investigate.

简短的搜索还揭示了用 Fortuna 替换默认实现的 Linux 补丁。我对此知之甚少,但欢迎您进行调查。

I should also mention that while it's very dangerous to use a badly implemented SecureRandomalgorithm and/or randomness source, you can roll your own JCE Provider with a custom implementation of SecureRandomSpi. You will need to go through a process with Sun to get your provider signed, but it's actually pretty straightforward; they just need you to fax them a form stating that you're aware of the US export restrictions on crypto libraries.

我还应该提到,虽然使用错误实现的SecureRandom算法和/或随机源非常危险,但您可以使用SecureRandomSpi. 您需要通过 Sun 的流程来让您的提供商签署,但这实际上非常简单;他们只需要你给他们传真一份表格,说明你知道美国对加密库的出口限制。

回答by erickson

My experience has been only with slow initialization of the PRNG, not with generation of random data after that. Try a more eager initialization strategy. Since they're expensive to create, treat it like a singleton and reuse the same instance. If there's too much thread contention for one instance, pool them or make them thread-local.

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

Don't compromise on random number generation. A weakness there compromises all of your security.

不要在随机数生成上妥协。那里的弱点会危及您的所有安全。

I don't see a lot of COTS atomic-decay–based generators, but there are several plans out there for them, if you really need a lot of random data. One site that always has interesting things to look at, including HotBits, is John Walker's Fourmilab.

我没有看到很多基于 COTS 原子衰变的生成器,但是如果您真的需要大量随机数据,那么有几个计划可以用于它们。John Walker 的 Fourmilab是一个总是有一些有趣的东西可供查看的站点,包括HotBits。

回答by Dan Dyer

On Linux, the default implementation for SecureRandomis NativePRNG(source code here), which tends to be very slow. On Windows, the default is SHA1PRNG, which as others pointed out you can also use on Linux if you specify it explicitly.

在 Linux 上,它的默认实现SecureRandomNativePRNG此处的源代码),这往往很慢。在 Windows 上,默认值为SHA1PRNG,正如其他人指出的那样,如果您明确指定,您也可以在 Linux 上使用它。

NativePRNGdiffers from SHA1PRNGand Uncommons Maths' AESCounterRNGin that it continuously receives entropy from the operating system (by reading from /dev/urandom). The other PRNGs do not acquire any additional entropy after seeding.

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

AESCounterRNG is about 10x faster than SHA1PRNG, which IIRC is itself two or three times faster than NativePRNG.

AESCounterRNG 大约比 快 10 倍SHA1PRNG,而 IIRC 本身比 快两到三倍NativePRNG

If you need a faster PRNG that acquires entropy after initialization, see if you can find a Java implementation of Fortuna. The core PRNG of a Fortuna implementation is identical to that used by AESCounterRNG, but there is also a sophisticated system of entropy pooling and automatic reseeding.

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

回答by Lorenzo Boccaccia

Use the secure random as initialization source for a recurrent algorithm; you could use then a Mersenne twister for the bulk work instead of the one in UncommonMath, which has been around for a while and proven better than other prng

使用安全随机作为循环算法的初始化源;然后你可以使用 Mersenne twiner 来完成大量工作,而不是 UncommonMath 中的那个,它已经存在了一段时间并且证明比其他 prng 更好

http://en.wikipedia.org/wiki/Mersenne_twister

http://en.wikipedia.org/wiki/Mersenne_twister

Make sure to refresh now and then the secure random used for the initialization, for example you could have one secure random generated per client, using one mersenne twister pseudo random generator per client, obtaining a high enough degree of randomization

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

回答by Chris Kite

If you want truly "cryptographically strong" randomness, then you need a strong entropy source. /dev/randomis slow because it has to wait for system events to gather entropy (disk reads, network packets, mouse movement, key presses, etc.).

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

A faster solution is a hardware random number generator. You may already have one built-in to your motherboard; check out the hw_random documentationfor instructions on figuring out if you have it, and how to use it. The rng-tools package includes a daemon which will feed hardware generated entropy into /dev/random.

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

If a HRNG is not available on your system, and you are willing to sacrifice entropy strength for performance, you will want to seed a good PRNG with data from /dev/random, and let the PRNG do the bulk of the work. There are several NIST-approved PRNG's listed in SP800-90which are straightforward to implement.

如果 HRNG 在您的系统上不可用,并且您愿意为了性能而牺牲熵强度,您将希望使用来自 的数据播种一个好的 PRNG /dev/random,并让 PRNG 完成大部分工作。SP800-90中列出了几个 NIST 批准的 PRNG, 它们易于实施。

回答by Diastrophism

Something else to look at is the property securerandom.source in file lib/security/java.security

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

There may be a performance benefit to using /dev/urandom rather than /dev/random. Remember that if the quality of the random numbers is important, don't make a compromise which breaks security.

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

回答by Jason S

It sounds like you should be clearer about your RNG requirements. The strongest cryptographic RNG requirement (as I understand it) would be that even if you know the algorithm used to generate them, and you know all previously generated random numbers, you could not get any useful information about any of the random numbers generated in the future, without spending an impractical amount of computing power.

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

If you don't need this full guarantee of randomness then there are probably appropriate performance tradeoffs. I would tend to agree with Dan Dyer's responseabout AESCounterRNG from Uncommons-Maths, or Fortuna (one of its authors is Bruce Schneier, an expert in cryptography). I've never used either but the ideas appear reputable at first glance.

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

I would thinkthat if you could generate an initial random seed periodically (e.g. once per day or hour or whatever), you could use a fast stream cipher to generate random numbers from successive chunks of the stream (if the stream cipher uses XOR then just pass in a stream of nulls or grab the XOR bits directly). ECRYPT's eStreamproject has lots of good information including performance benchmarks. This wouldn't maintain entropy between the points in time that you replenish it, so if someone knew one of the random numbers and the algorithm you used, technically it might be possible, with a lot of computing power, to break the stream cipher and guess its internal state to be able to predict future random numbers. But you'd have to decide whether that risk and its consequences are sufficient to justify the cost of maintaining entropy.

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

Edit: here's some cryptographic course notes on RNGI found on the 'net that look very relevant to this topic.

编辑:这是我在网上找到的一些关于 RNG 的密码学课程笔记,看起来与这个主题非常相关。

回答by Thomas Leonard

You should be able to select the faster-but-slightly-less-secure /dev/urandom on Linux using:

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

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

However, this doesn't work with Java 5 and later (Java Bug 6202721). The suggested work-around is to use:

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

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

(note the extra /./)

(注意额外的/./