Terse Systems

The Right Way to Use SecureRandom

| Comments

How do you generate a secure random number in JDK 1.8? It depends.

The default:

1
2
3
SecureRandom random = new SecureRandom();
byte[] values = new byte[20];
random.nextBytes(values);

If you’re okay with blocking the thread:

1
2
3
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:

  1. NativePRNG** Sun
  2. SHA1PRNG** Sun
  3. NativePRNGBlocking Sun
  4. 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:

1
2
3
4
$ 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:

1
2
3
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:

1
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:

1
-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:

1
2
3
4
5
6
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.

1
2
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:

Also, the story of Android’s OpenSSL PRNG compromise for bitcoin theft is fascinating:

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