How do you generate a secure random number in JDK 1.8? It depends.
The default:
SecureRandom random = new SecureRandom();
byte[] values = new byte[20];
random.nextBytes(values);
If you're okay with blocking the thread:
SecureRandom random = SecureRandom.getInstanceStrong();
byte[] values = new byte[20];
random.nextBytes(values);
That's really it.
Details
The difference between the first use case and the second: the first instance uses /dev/urandom
. The second instance uses /dev/random
. /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/random
over /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 /dev/urandom
.
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
- NativePRNGNonBlocking
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)
The nextBytes
method is the base method: when you call nextInt
or 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 securerandom.strongAlgorithms
:
securerandom.strongAlgorithms=NativePRNGBlocking:SUN
This means that SecureRandom.getInstanceStrong()
is equivalent to SecureRandom.getInstance("NativePRNGBlocking")
.
Again, /dev/random
is no more secure than /dev/urandom
.
Debugging
If you want to see what your SecureRandom implementation is doing, then you can turn on debugging:
-Djava.security.debug="provider,engine=SecureRandom"
There is a debugging page but this is still undocumented in 1.8. It's fixed in JDK 1.9.
Using SHA1PRNG
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[20];
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 /dev/urandom
.
Further Reading
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.
Comments