My previous blog entry on SecureRandom was Issues to be aware of when using Java’s SecureRandom. Today, I’ll write about the most complex SecureRandom implementation I’ve seen so far.
This implementation is the default SecureRandom implementation in Oracle JRE installations on Windows. If it is configured to not be the default, it can be instantiated using:
Initial Seeding (Default)
This implementation of SecureRandom can be seeded using two mechanisms (when default configurations are used – non-default configurations will be discussed later):
- If a seed is not specified when instantiating a SecureRandom object, and the caller attempts to get output from the PRNG, it seeds itself automatically. It seeds itself using a static SecureRandom instance that is a member variable in the class. The static instance is seeded using sun.security.provider.SeedGenerator, which may get entropy from an external source. This means that this implementation only retrieves entropy from an external source once; any SecureRandom instances that are created afterwards are seeded using outputs from the static SecureRandom instance. By default, the static SecureRandom instance is seeded as follows:
- On Windows, SeedGenerator.getSystemEntropy() as well as the Windows CryptGenRandom() function are used.
- On *nix, SeedGenerator.getSystemEntropy() as well as /dev/random are used. Note that since reads from /dev/random can block if insufficient entropy is available, the first time a SecureRandom object is seeded, the thread attempting to initialize the object could hang.
- If a seed is specified using setSeed() before any output is obtained from the PRNG, only the specified seed will be used for the PRNG, and the self-seeding mechanism using the static SecureRandom instance will be bypassed.
The output of the SHA1PRNG is generated using applications of the SHA-1 hash function.
Other Seed Generation (Default)
If generateSeed() is called, the Windows CryptGenRandom() function is used to generate a seed on Windows, and /dev/random is used to generate a seed on *nix by default. Since reads from /dev/random can block if insufficient entropy is available, the seed generation operation on *nix could hang.
Note that unlike what the Javadocs at http://docs.oracle.com/javase/7/docs/api/java/security/SecureRandom.html#generateSeed(int) state, the mechanism used to generate this seed is not the same as the mechanism used to seed SecureRandom instances as described in an earlier section. It is similar to the way in which the static SecureRandom instance is seeded; however, SeedGenerator.getSystemEntropy() is not used here.
Seed Generation (Non-Default Configurations)
The sun.security.provider.SecureRandom implementation contains a static SecureRandom instance that is used to seed SHA1PRNG instances generated for callers. The static instance is seeded using sun.security.provider.SeedGenerator. Depending on the Java Virtual Machine’s configuration, the actual seeding mechanism may vary. The various mechanisms are described in the subsections below.
Regardless of the configuration, the SeedGenerator.getSystemEntropy() method is always used as part of the seed. This method outputs a SHA-1 hash of the following:
- One byte from System.currentTimeMillis();
- All system properties (name/value pairs)
- List of files/directories in java.io.tmpdir
- Exception.hashCode (if an Exception occurs while gathering any of the above values; if an Exception does occur, some of the above values may not be included in the hash)
- Runtime.getRuntime. freeMemory()
The following values are used to determine where the rest of the seed entropy is obtained:
- The java.security.egd system property
- The securerandom.source property in the java.security file
The java.security.egd system property takes precedence. By default, java.security.egd is not defined, and securerandom.source is set to file:/dev/urandom. The value set by these properties will be referred to as egdSource below. The seeding mechanisms are chosen as follows:
- If egdSource is set to file:/dev/urandom or file:/dev/random, then sun.security.provider.NativeSeedGenerator is used.
- If egdSource is set to some other value, then the given file/URL is used with sun.security.provider.SeedGenerator.URLSeedGenerator.
- If egdSource is not specified or if the mechanism chosen above fails, then sun.security.provider.SeedGenerator.ThreadedSeedGenerator is used.
The *nix implementation uses sun.security.provider.SeedGenerator.URLSeedGenerator with /dev/random. This results in the seed being read from /dev/random. Note that since reads from /dev/random can block if insufficient entropy is available, the seed generation operation on *nix could hang.
The Windows implementation uses the Windows CryptGenRandom() function to generate the seed.
This implementation reads from the given URL (which could be a local file) and returns the first bytes at the URL as the seed.
This implementation spawns a thread that runs for approximately 250ms, and counts the number of times a loop executes while the thread is running. This results in one byte of unpredictable output being generated. This seed generator is quite slow and generates at most 4 bytes per second. Seeding a SHA1PRNG instance using this mechanism takes at least 5 seconds.
The sun.security.provider.SecureRandom implementation is the most complex SecureRandom implementation I’ve seen so far in terms of the number of ways in which it can be configured and used. In general, it works as expected, but there are a few configuration and usage issues to be aware of:
- Always obtain some output from the PRNG before calling setSeed()
- On *nix, be aware that the creating the very first instance of SecureRandom or calling generateSeed() could cause your thread to hang – if the default configuration is used
- If egdSource is set to a file/URL with predictable data, the outputs of SecureRandom could become predictable
- If egdSource is set to an invalid file/URL, a very slow seeding mechanism is used as a backup that could cause a temporary Denial of Service