Using the SecureRandom Class

Categories: Java

(back to main post)

Introduction

The java.security.SecureRandom class seems so simple and trivial to use. Looks can be very deceptive.

Site thesaurus.com suggests many alternatives to “random” including “arbitrary”, “irregular”, “odd”, “erratic” and “unpredictable”. All good descriptions of the SecureRandom API and implementation.

The primary intent of the SecureRandom class is to generate truly unpredictable sequences of bytes or integers. It isn’t meant for producing large amounts of data, or having high performance, but who needs large amounts of random data anyway?

The alternative java.util.Random class is much “lighter weight”, working faster but producing less-random outputs than SecureRandom.

Unfortunately, the SecureRandom class has significant platform-specific behaviour, and rather ambigious javadocs that can lead to some traps. The most dangerous behaviour is that on Linux it can intermittently block for minutes at a time, which can make application users a little unhappy.

Applicable Java Versions

The behaviour described below appears to be the same on all Java versions from at least 6.0 through to at least 8.0 beta-releases.

Creating an Instance

An instance can be created via “new”, or can be obtained via the static method SecureRandom.getInstance(String algorithm).

Using new simply returns the “default” implementation; the sun.security.provider.Sun “crypto provider” is the highest-priority “crypto provider” by default, and it:

  • registers the “NativePRNG” algorithm if it is available on the current platform, and
  • always registers the “SHA1PRNG” implementation.

The default is therefore the Sun NativePRNG if it is applicable, else the Sun SHA1PRNG.

There are no other standard implementations shipped in the Sun crypto provider; NativePRNG and SHA1PRNG are it.

The API

The Random class defines:

  • setSeed(long)
  • a bunch of next{type} methods

The next* methods are all just wrappers around the nextBytes(byte[]) method.

The SecureRandom class adds:

  • generateSeed, which returns an array of bytes
  • a static getSeed method, which is just a wrapper around generateSeed()

The generateSeed() method is meant to return truly random data, while the next* methods are less clearly defined, and are typically pseudo-random.

The setSeed() method is something to be wary of: the behaviour depends on the underlying SecureRandom implementation, and on whether the object has already been seeded (explicitly or implicitly). See the next sections for a definition of the behaviour in each case.

A quick note about /dev/random and /dev/urandom

On Linux, the operating system exposes two kernel-level random number generators to userspace as files.

The /dev/random file can be read by anyone, and returns truly random numbers or as good as the kernel can make them. Random numbers are based upon unpredictable things such as the times at which disk-drive interrupts occur, or network packets arrive, or mouse and keyboard events. The intention is to gather data in a way that a remote or local attacker cannot well predict. The values are then mixed together and fed through an appropriate algorithm to generate an “entropy pool” which reads of /dev/random draws from. Unfortunately while this approach generates very high-quality randomness, it doesn’t do it quickly. Reading lots of data from this file will “exhaust the entropy pool”. At this point the kernel simply makes all further reads block until more entropy arrives, on the principal that when doing security-sensitive things, not working is better than working incorrectly. This file is shared across all applications on the system, so different applications can indirectly cause others to “block” while reading this file.

The /dev/urandom file is more forgiving; it uses a pseudo-random-number-generation algorithm which it “reseeds” from time to time from the /dev/random data. Reads of this file never block.

Question: are there per-process limits on read rates from the /dev/random file?

Microsoft Windows apparently has a “Crypto API” that provides random numbers for windows-based programs. I am not aware of the details of this, and in particular whether it also “blocks” if truly random data cannot be guaranteed.

The NativePRNG Algorithm on Linux

Despite this being the default implementation of SecureRandomSpi on Linux (normally), you won’t find any documentation on its behaviour in the standard javadocs, because it is “just a pluggable implementation” in a sun.* package. Hey, who needs to know about the details, right?

It actually does the following:

  • the algorithm is implemented in class sun.security.provider.NativePRNG (which subclasses SecureRandomSpi)
  • a singleton instance of that class is shared across all SecureRandom instances
  • it opens the /dev/random and /dev/urandom files and keeps them open
  • it creates an instance of the SHA1PRNG random generator and seeds it with 20 bytes read from /dev/urandom

  • generateSeed() reads bytes directly from /dev/random
  • nextBytes(byte[]) reads bytes from both /dev/urandom and from the prng, and xors them together
  • setSeed() is passed through to the SHA1PRNG helper object

Despite the name, the class is not a pseudo-random-number generator (at least not itself, although /dev/urandom is). It therefore never needs to “seed itself”. And the helper “prng” object is seeded from /dev/urandom which does not block. As a result, it will never block unless generateSeed() is invoked.

As noted above, the setSeed() method simply sets the seed for the “helper” prng object used by the nextBytes method. Because this data is mixed with data from /dev/urandom, setting the seed never results in repeatable sequences of output values. And setSeed has absolutely no effect on data returned by the generateSeed() method.

The helper isn’t really needed, but without it the setSeed() calls would simply be ignored, which isn’t very nice. So it is used to “enhance” the data from nextBytes.

In summary: if you create a SecureRandom of type NativePRNG then its next* methods always read from /dev/urandom and never block. If you use its generateSeed method, then it can block - but if you pass it around as parent type Random (which does not have the generateSeed method) then blocking cannot happen (without downcasts).

Whether this class is the default can be changed via properties inside the JRE, and even a commandline option. See later.

The SHA1PRNG Algorithm On All Platforms

This is the default algorithm on Microsoft Windows platforms, and can be explicitly requested on Linux platforms.

The concrete class implementing the “SHA1PRNG” algorithm is sun.security.provider.SecureRandom (which subclasses SecureRandomSpi). It does the following:

  • generateSeed method delegates to a singleton “SeedGenerator” helper
  • nextBytes causes the object to seed itself if necessary, and then uses an internal PRNG algorithm
  • setSeed initialises the object if not already seeded (ie avoids use of SeedGenerator in nextBytes), otherwise “mixes in” the additional data to the existing PRNG seed.

In effect, the main class implements the pseudo-random-number algorithm, and delegates the rest of the work (generating truly random numbers) to a SeedGenerator helper class. The SeedGenerator is a singleton (ie shared across all SHA1PRNG SecureRandom objects) that itself delegates to one of:

  • NativeSeedGenerator() if property “securerandom.source” is /dev/random or /dev/urandom
  • URLSeedGenerator(url) if property “securerandom.source” is something else
  • ThreadedSeedGenerator otherwise

ThreadedSeedGenerator attempts to generate random data by starting a pool of threads which race against each other in ways determined by the operating system’s thread scheduling and load. Quite what the results are, nobody can predict - which is the point. This is the adhoc fallback algorithm when nothing better is available.

On Windows, NativeSeedGenerator is a wrapper around the Windows Crypto API.

On Linux, NativeSeedGenerator is actually a trivial subclass of URLSeedGenerator and is effectively URLSeedGenerator(“file://dev/random”).

URLSeedGenerator opens the specified URL and reads data from it. Interestingly, this gives the possibility of specifying random data seeds for testing by just pointing the URLSeedGenerator at a fixed (and sufficiently long) file of data.

Note that setSeed() has absolutely no effect on the return values for generateSeed().

Note also that generateSeed() can block, and that calling nextBytes without having explicitly seeded the SecureRandom instance can also block (because it seeds itself via generateSeed along with other data from SeedGenerator.getSystemEntropy) if the NativeSeedGenerator is being used, which is the case by default.

Seeding

An instance of SHA1PRNG initially has no seed. If setSeed() is called on it, this sets the seed used, and numbers returned by the nextBytes method (and all those next* methods derived from it) then become repeatable if the same seed is reused.

If setSeed() is called on an instance that is already seeded, then the new value is just “mixed in” to the existing seed.

If nextBytes is invoked before the object is explicitly seeded, then the generateSeed method (and therefore the SeedGenerator helper) will be used (together with other values) to generate the initial prng seed. This may block if the SeedGenerator is reading from /dev/random.

Summary

In summary: if you create a SecureRandom of type SHA1PRNG then its next* methods always derive values from the existing “seed”, and don’t block as long as the object is already seeded. However if the object isn’t seeded then the first call will trigger a read of /dev/random (and therefore might block) if NativeSeedGenerator is being used. The generateSeed method always reads from /dev/random if the NativeSeedGenerator is used.

Configuring the “securerandom.source” property

As noted above, the SHA1PRNG algorithm uses some configuration properties to determine where its SeedGenerator actually gets its data from.

File $JRE_HOME/lib/security/java.security has an optional property “securerandom.source”; by default on both Windows and Linux installations this has the value “file:/dev/urandom”.

This is misleading for several reasons:

On Linux:

  1. when this value is “file:/dev/urandom” then the NativePRNG algorithm is registered by the Sun crypto provider as the default implementation; the NativePRNG algorithm then reads from /dev/urandom for nextBytes but /dev/random for generateSeed
  2. when this value is “file:/dev/random” then the NativePRNG algorithm is not registered by the Sun crypto provider, but the SHA1PRNG system uses a NativeSeedGenerator which reads from /dev/random.
  3. when this value is anything else then the SHA1PRNG is used with a URLSeedGenerator that reads from that source.
  4. when the value is undefined, then SHA1PRNG is used with ThreadedSeedGenerator
  5. when the code explicitly asks for “SHA1PRNG” and the value is either “file:/dev/urandom” or “file:/dev/random” then (2) also occurs
  6. when the code explicitly asks for “SHA1PRNG” and the value is some other “file:” url, then (3) occurs
  7. when the code explicitly asks for “SHA1PRNG” and the value is undefined then (4) occurs

On Windows systems:

  1. when the value is “file:/dev/urandom” then SHA1PRNG is used with the MS Crypto API
  2. when the value is any other “file:” url, then SHA1PRNG is used with a URLSeedGenerator that reads from that source
  3. when the value is not defined, then SHA1PRNG is used with ThreadedSeedGenerator.

And “-Djava.security.egd” can be used to override the above property. Question: does “” emulate removing the property?

It’s all pretty complicated.

The most interesting result is that deleting this property completely will force SHA1PRNG with the inbuild ThreadedSeedGenerator which never blocks.

Another interesting result is that a commandline option “-Djava.security.egd=file:/tmp/randomNums” can be used to provide a file of random numbers that will be used as seeds.

The -D option can’t be used on linux to redirect /dev/random reads to /dev/urandom: “-Djava.security.egd=file:/dev/urandom” still triggers the NativePRNG which reads /dev/random (linux). However the following works:

sudo ln /dev/urandom /dev/fakerandom
java -Djava.security.egd=file:/dev/fakerandom

With the above hack, the URLSeedGenerator is instantiated and reads from /dev/fakerandom (ie /dev/urandom) and then never blocks. Note: requires SHA1PRNG be specified in the code first :-(

Note also that the -Djava.security.egd option overrides the setting in $JRE_HOME/lib/security/java.security.

Inconsistencies

As noted, the NativePRNG algorithm for Linux will never block in the nextBytes() method; it always reads from /dev/urandom.

However the SHA1PRNG algorithm on Linux can block in the nextBytes() method if the SecureRandom.setSeed() method has not been invoked, as it tries to seed itself using generateSeed() which in turn reads from /dev/random.

The NativePRNG algorithm will never return predictable sequences of bytes; calling “setSeed” does affect the nextBytes output, but not in a predictable way.

The SHA1PRNG algorithm will return predictable sequences of bytes if setSeed is invoked before the first call to nextBytes.

The NativePRNG algorithm is not at all configurable; generateSeed comes from /dev/random and nextBytes comes from /dev/urandom.

The SHA1PRNG algorithm is quite configurable; it can be forced to use the adhoc ThreadedSeedGenerator by editing a file in the JRE directory; it can be forced to use a standard file on the filesystem via a “-D” commandline option. And with some trickery, it can be forced to use /dev/urandom even for generateSeed.

Consistencies

Both implementations can block in the generateSeed method if the underlying source of randomness is /dev/random (which it is by default for both algorithms on Linux). Well, unless a trick is used to force SHA1PRNG to use /dev/urandom, in which case it will not block. So not consistent at all then.

References

comments powered by Disqus