TL;DR
If running Java 8 on modern OSes with support to Deterministic Random Bit Generator (DRBG), I'd recommend using
-Djava.security.egd=file:/dev/urandom
to avoid getting the code blocked unexpectedly. If not sure about the OS being used, my suggestion is to stick with the original recommendation, namely:
-Djava.security.egd=file:/dev/./urandom
If running Java 11, I'd recommend simply using
-Djava.security.egd=file:/dev/./urandom
to make sure of:
- leveraging the strongest SecureRandom implementation available (DRBG) regardless the underpinning platform
- avoiding getting the code blocked unexpectedly (
securerandom.source=file:/dev/urandom
)
Read on to know the details.
Java applications can and should use java.security.SecureRandom class to produce cryptographically strong random values by using a cryptographically strong pseudo-random number generator (CSPRNG). The standard JDK implementations of java.util.Random class are not considered cryptographically strong.
Unix-like operating systems have /dev/random
, a special file which serves pseudo random numbers accessing environmental noise collected from device drivers and other sources. However, it blocks if there is less entropy available than requested; /dev/urandom
typically never blocks, even if the pseudorandom number generator seed was not fully initialized with entropy since boot. There still is a 3rd special file, /dev/arandom
which blocks after boot until the seed has been securely initialized with enough entropy, and then never blocks again.
By default, the JVM seeds the SecureRandom class using /dev/random
, therefore your Java code can block unexpectedly. The option -Djava.security.egd=file:/dev/./urandom
in the command line invocation used to start the Java process tells the JVM to use /dev/urandom
instead.
The extra /./
seems to make the JVM to use the SHA1PRNG algorithm which uses SHA-1 as the foundation of the PRNG (Pseudo Random Number Generator). It is stronger than the NativePRNG algorithm used when /dev/urandom
is specified.
Finally, there is a myth that /dev/urandom
is a pseudo random number generator, a PRNG, whilst /dev/random
is a “true” random number generator. This is simply not true, both /dev/random
and /dev/urandom
are fed by the same CSPRNG (cryptographically secure pseudorandom number generator). Only their behaviour differs: /dev/random
blocks when its randomness pool runs out of entropy according to some estimate, whilst /dev/urandom
does not.
What about systems with low entropy? It isn't that bad.
It turns out that “looking random” is the basic requirement for several cryptographic components such as webserver's ephemeral session keys. And if you take the output of a cryptographic hash, it is indistinguishable from a random string so that ciphers will accept it. That's the reason of using the SHA1PRNG algorithm, as it uses a hash function and a counter, together with a seed.
When is supposed to be applied?
Always, I'd say.
Sources:
https://gist.github.com/svrc/5a8accc57219b9548fe1
https://www.2uo.de/myths-about-urandom
EDIT 09/2020:
I have changed this update to reflect the tests with:
-Java 8 on modern OSes
-Java 11 as it is the currently long-term support (LTS) version.
A comment mentions a change on SecureRandom class' behaviour in Java 8.
SHA1PRNG and NativePRNG were fixed to properly respect the SecureRandom seed source properties in the java.security file. (The obscure workaround using file:///dev/urandom and file:/dev/./urandom is no longer required.)
This had already been pointed out by the tests referenced on the Sources section above. The extra /./
is required to change the algorithm used by SecureRandom in Java 8 from NativePRNG to SHA1PRNG.
I agree that NativePRNG is more secure than SHA1PRNG, but only when running on modern OSes. I have therefore updated accordingly my conclusion and moved it to the top.
However, I do have some news that I'd like to share. As per the JEP-273, since Java 9 the SecureRandom class implements the three Deterministic Random Bit Generator (DRBG) mechanisms described in NIST 800-90Ar1. These mechanisms implement modern algorithms as strong as SHA-512 and AES-256.
The JDK previously had two kinds of SecureRandom implementations:
- One is platform-dependent and based on native calls or OS devices
such as reading
/dev/{u}random
on Unix or using the CryptoAPI on
Windows. The latest releases of Linux and Windows already
support DRBG, but older releases and embedded systems might not.
- The other kind is a pure Java implementation that uses an older
SHA1-based RNG implementation, which is not as strong as the
algorithms used by approved DRBG mechanisms.
Meanwhile the Java 11 Security Developer’s Guide still reads
On Linux and macOS, if the entropy gathering device in java.security
is set to file:/dev/urandom
or file:/dev/random
, then NativePRNG is
preferred to SHA1PRNG. Otherwise, SHA1PRNG is preferred.
To clarify how the new DRBG mechanisms play together with the previous PRNGs, I ran some tests on macOS (Darwin) with AdoptOpenJDK (build 11.0.7+10). Here are the results:
-Djava.security.egd=file:/dev/random
(This equals the default option)
Default algorithm: NativePRNG
Provider: SecureRandom.NativePRNG algorithm from: SUN
-Djava.security.egd=file:/dev/urandom
Default algorithm: NativePRNG
Provider: SecureRandom.NativePRNG algorithm from: SUN
-Djava.security.egd=file:/dev/./urandom
Default algorithm: DRBG
Provider: SecureRandom.DRBG algorithm from: SUN
Finally, the point of using /dev/urandom
as source of randomness still remains paramount even when using modern OSes, as we can read on this very interesting post:
Sharing /dev/random
is a challenge for any Linux container technology...
The low amount of entropy on virtualized servers problem is exacerbated because ... Linux Containers running on the same host compete for a limited supply of entropy. This type of problem is sometimes referred to as a stampeding herd. The /dev/random
device is a scarce shared system resource that Linux Container tenants likely have not realised they are sharing. When they all try to use it at the same time they are effectively causing a denial of service on each other.
Sources:
https://www.openssl.org/blog/blog/2017/08/12/random/