How do you generate a secure random number in JDK 1.8? It depends.
SecureRandom random = new SecureRandom(); byte values = new byte; random.nextBytes(values);
If you're okay with blocking the thread:
SecureRandom random = SecureRandom.getInstanceStrong(); byte values = new byte; random.nextBytes(values);
That's really it.
The difference between the first use case and the second: the first instance uses
/dev/urandom. The second instance uses
/dev/random blocks the thread if there isn't enough randomness available, but
/dev/urandom will never block.
Believe it or not, there is no advantage in using
/dev/urandom. They use the same pool of randomness under the hood. They are equally secure. If you want to safely generate random numbers, you should use
The only time you would want to call
/dev/random is when the machine is first booting, and entropy has not yet accumulated. Most systems will save off entropy before shutting down so that some is available when booting, so this is not an issue if you run directly on hardware.
However, it might be an issue if you don't run directly on hardware. If you are using a container based solution like Docker or CoreOS, you may start off from an initial image, and so may not be able to save state between reboots – additionally, in a multi-tenant container solution, there is only one shared
/dev/random which may block horribly. However, the work around in these cases is to seed
/dev/random with a userspace solution, either using an entropy server for pollinate, or a CPU time stamp counter for haveged. Either way, by the time the JVM starts, the system's entropy pool should already be up to the job.
Some people have a cryptographic habit of using
/dev/random for seed generation, so there are some cases where it's easier to use
getInstanceStrong just to avoid argument or the hassle of a code review. However, that's a workaround for a personnel issue, not a cryptographic argument.
How the default works
There is a full list of SecureRandom implementation available, which lists the preferences available for the "default" SecureRandom. For Linux and MacOS, the list is:
- NativePRNG** Sun
- SHA1PRNG** Sun
- NativePRNGBlocking Sun
There is an asterisk saying "On Solaris, Linux, and OS X, 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."
However, this doesn't affect the list. When they say "entropy gathering device", they mean "securerandom.source", and grepping through java.security shows:
$ grep securerandom.source $JAVA_HOME/jre/lib/security/java.security # specified by the "securerandom.source" Security property. If an # "securerandom.source" Security property. securerandom.source=file:/dev/random
Yep, the line exists, so "NativePRNG" is preferred to "SHA1PRNG". So, what does that mean? There's an entry in Standard Names, but there's also is a more specific note of what each algorithm does in the Sun Providers section:
- SHA1PRNG (Initial seeding is currently done via a combination of system attributes and the java.security entropy gathering device)
- NativePRNG (nextBytes() uses /dev/urandom, generateSeed() uses /dev/random)
- NativePRNGBlocking (nextBytes() and generateSeed() use /dev/random)
- NativePRNGNonBlocking (nextBytes() and generateSeed() use /dev/urandom)
nextBytes method is the base method: when you call
nextLong, etc, it will call down to
nextBytes under the hood. The
generateSeed method is not needed for a Native PRNG of any type, but it IS useful to seed a user space PRNG such as SHA1PRNG. You can call
setSeed on a NativePRNG, and it will use an internal SHA1PRNG that can be "mixed in" with
/dev/urandom, but it's not necessary.
The default SecureRandom, which is the no-args constructor version, will use "NativePRNG" under the hood:
final SecureRandom defaultRandom = new SecureRandom(); System.out.println("defaultRandom algorithm = " + defaultRandom.getAlgorithm()); // prints "defaultRandom algorithm = NativePRNG"
And then you're golden: you have all the randomness you need, and you will never block.
How getInstanceStrong works
The getInstanceStrong method will call the strongest crypto source available on the system. It does this by looking for the
$JAVA_HOME/jre/lib/security/java.security file, and looking up the value in
This means that
SecureRandom.getInstanceStrong() is equivalent to
/dev/random is no more secure than
If you want to see what your SecureRandom implementation is doing, then you can turn on debugging:
If you're on Windows, then SHA1PRNG is the preferred algorithm – however, the underlying NativeSeedGenerator called by
generateSeed() calls out to the Windows Crypto API. Alternately, you may have to work with a library that explicitly checks for the SecureRandom algorithm of
SHA1PRNG, as is mentioned in the code sample for PRNGFixes.
SHA1PRNG is a pure Java implementation which is not as strong as the algorithms used by approved DRBG mechanisms in NIST SP800-90.
If you have a SHA1PRNG, then you can seed it directly:
SecureRandom nativeRandom = SecureRandom.getInstance("NativePRNGNonBlocking"); // assuming Unix byte seed = nativeRandom.generateSeed(55); // NIST SP800-90A suggests 440 bits for SHA1 seed SecureRandom sha1Random = SecureRandom.getInstance("SHA1PRNG"); sha1Random.setSeed(seed); byte values = new byte; sha1Random.nextBytes(values); // SHA1PRNG, seeded properly
Or, if you just call nextBytes immediately, the very first call will self-seed from
/dev/random with 20 bytes… which will block, but what the heck.
SecureRandom sha1Random = SecureRandom.getInstance("SHA1PRNG"); sha1Random.nextBytes(values); // SHA1PRNG, self seeded with 20 bytes from blocking OS
You can futz with the self-seeding by changing the entropy generator around, but it's not really worth it unless you have code you can't change. My recommendation is to use "NativePRNGNonBlocking" and have done with it.
Randomness problems with SHA1PRNG
SHA1PRNG is pseudo random. You can actually measure the amount of randomness in the system, and it turns out that if you run SHA1PRNG with a weak seed, the amount of randomness is traceable.
Yongge Wang and Tony Nicol wrote a fascinating paper On statistical distance based testing of pseudo random sequences and experiments with PHP and Debian OpenSSL – the Java bit is at "8.1. Java SHA1PRNG API based sequences". They also have a statistical testing package and software at LILTest.
In practice, if you have any doubts about SHA1PRNG, you should use
For more of an overview of how SecureRandom works under the hood, please see:
JDK 1.8 made a number of security improvements. SecureRandom was JEP-123, and the developer logs go into sordid history:
- JEP 123
- JEP 123: SecureRandom First Draft and Implementation
- Update #6: JEP 123: SecureRandom Draft and Implementation.
- RFR 8066397 Remove network-related seed initialization code in ThreadLocal/SplittableRandom
- Use /dev/urandom rather than /dev/random if it exists. – Read the comments!
- Using the new JDK 8 Security Features
Also, the story of Android's OpenSSL PRNG compromise for bitcoin theft is fascinating:
- All Android-created Bitcoin wallets vulnerable to theft
- Some SecureRandom Thoughts
- (In)Security of Java SecureRandom Implementations
There is a new version of SecureRandom coming in JDK 1.9, JEP-273, which adds SHA-512 and AES-256 based off NIST SP800-90.More …