There have been several changes to Java’s SecureRandom API since creating this post back in 2009. According to Oracle, the following interesting changes have been made:
getInstanceStrong()
method was introduced in JDK 8. This method returns an instance of the strongest SecureRandom implementation available on each platform.Let’s take a look at each of these changes and see what they mean for your applications. Note that these details only apply to the Oracle JDK/JRE that is distributed with the SUN and SunMSCAPI Java Cryptography Extension providers.
The previous behavior of NativePRNG on Solaris/Linux/macOS was that sun.security.provider.NativePRNG.generateSeed()
could block because it used /dev/random, but getting outputs using nextBytes()
, nextLong()
, etc. would never block because they used /dev/urandom (this was true for default environment settings). NativePRNG still behaves like this, but there are now two additional implementations:
SHA1PRNG
instance using 20 bytes from /dev/randomnextBytes()
, nextInt()
, etc.: This provides the XOR of the output from the internal SHA1PRNG
instance (see above) and data read from /dev/randomgetSeed()
: This provides data read from /dev/randomNativePRNGNonBlocking
uses /dev/urandom for all of the following operations:
SHA1PRNG
instance using 20 bytes from /dev/urandomnextBytes()
, nextInt()
, etc.: This provides the XOR of the output from the internal SHA1PRNG
instance (see above) and data read from /dev/urandomgetSeed()
: This provides data read from /dev/urandomNativePRNGBlocking
will be problematic in any applications where you do not want your application to block while the system gathers more entropy. Getting any type of output from it could cause your application’s thread to stop responding. It is fine for use in a desktop application for generating a local cryptographic key (for example), but will almost never be okay to use in a web application.
Note that NativePRNGBlocking
can be somewhat wasteful in its entropy usage. For performance reasons, it will read in 32 bytes of data at a time from /dev/random for nextBytes()
, nextInt()
, etc. Any data that doesn’t get used within 100 milliseconds will be discarded. As a result, each call to nextBoolean()
will give you one bit of output, but the implementation may actually use up (and mostly discard) 256 bits of entropy.
This method uses the securerandom.strongAlgorithms
property in the java.security
file to select a SecureRandom implementation.
If you simply use the old mechanism to get the highest priority SecureRandom implementation (i.e. new java.security.SecureRandom()
), then you will get the following implementations depending on your platform:
Operating System | Default SecureRandom Implementation | Notes |
Windows | SHA1PRNG (sun.security.provider.SecureRandom) | Calling setSeed() before getting output results in predictable outputs as discussed in https://www.synopsys.com/blogs/software-security/proper-use-of-javas-securerandom/ |
Solaris/Linux/macOS | NativePRNG (sun.security.provider.NativePRNG) | Some method calls can block as discussed in https://www.synopsys.com/blogs/software-security/issues-when-using-java-securerandom/ |
If you use SecureRandom.getInstanceStrong()
, then the following defaults are used instead:
Operating System | Default SecureRandom Implementation | Notes |
Windows | Windows-PRNG (sun.security.mscapi.PRNG) | This simply outputs bytes from the Windows CryptGenRandom() API |
Solaris/Linux/macOS | NativePRNGBlocking (sun.security.provider.NativePRNG$Blocking) | See the previous section for details |
Because of this default behavior, you should avoid using SecureRandom.getInstanceStrong()
in any server-side code running on Solaris/Linux/macOS where availability is important.
Aug. 14, 2009 @ 9:54 a.m.
When generating random numbers in Java for cryptographic purposes, many developers often use the java.security.SecureRandom
class. And while the java.security.SecureRandom
class is designed to generate cryptographically secure random numbers, there are a few subtleties in the API, and if it is used improperly the output can become predictable. At Synopsys we have witnessed a number of cases where this is true. The following is a guide to the proper use of Java’s java.security.SecureRandom
class.
First, let’s take a quick look at how the java.security.SecureRandom
API works. The java.security.SecureRandom
class does not actually implement a pseudorandom number generator (PRNG) itself. It uses PRNG implementations in other classes to generate random numbers. A number of actual PRNGs may actually be used when an instance of java.security.SecureRandom
is created. The PRNGs are part of Java cryptographic service providers (CSPs). In Sun’s Java implementation, the SUN CSP is used by default. On Windows, the SUN CSP uses the SHA1PRNG implemented in sun.security.provider.SecureRandom by default. On Solaris and Linux, the SUN CSP default is to use sun.security.provider.NativePRNG which simply provides the output of the /dev/urandom
PRNG provided by the operating system. If however, on Solaris/Linux, the java.security configuration file in the JRE is modified such that securerandom.source is set to something other than file:/dev/urandom
, then the SHA1PRNG implemented in sun.security.provider.SecureRandom is used, as on Windows. Of course, an application can choose not to use the defaults, and can always specify a particular PRNG implemented by a particular cryptographic provider. In Synopsys’ experience, most Java applications that use java.security.SecureRandom
actually use the SHA1PRNG provided by the SUN CSP under the cover.
Now, an application will end up with an instance of SHA1PRNG implemented in sun.security.provider.SecureRandom
using the following calls:
// The following will create SUN SHA1PRNG on Windows with // default configuration and Sun JRE, and on Solaris/Linux // if securerandom.source is modified in java.security SecureRandom sr1 = new SecureRandom(); // The following will create SUN SHA1PRNG if the highest // priority CSP is SUN SecureRandom sr2 = SecureRandom.getInstance("SHA1PRNG"); // The following will always create SUN SHA1PRNG SecureRandom sr3 = SecureRandom.getInstance("SHA1PRNG", "SUN");
Note that according to Sun’s documentation, the returned java.security.SecureRandom
instance is not seeded by any of these calls. If after one of these calls, java.security.SecureRandom.nextBytes(byte[])
is called, then the PRNG is seeded using a secure mechanism provided by the underlying operating system (starting with JRE 1.4.1 in Windows and JRE 1.4.2 in Linux and Solaris). If java.security.SecureRandom.setSeed(long)
or java.security.SecureRandom.setSeed(byte[])
is called before a call to java.security.SecureRandom.nextBytes(byte[])
, then the internal seeding mechanism is bypassed, and only the provided seed is used to generate random numbers.
For those who are not familiar with the inner workings of cryptographic PRNGs, their job is to take a relatively small random seed and use it to produce deterministic output that seems random to anybody that does not know what the seed is. The PRNG tries to ensure that the output does not reveal any information about the seed, and that somebody observing the output cannot predict future outputs without knowing the seed.
By bypassing the internal secure seeding mechanism of the SHA1PRNG, you may compromise the security of your PRNG output. If you seed it with anything that an attacker can potentially predict (e.g. the time when the PRNG instance was created), then using java.security.SecureRandom
may not provide the level of security that you need.
Finally, regardless of how well the PRNG is seeded, it should not be used indefinitely without reseeding. There are two approaches that can be used for longer-term security of PRNG output:
java.security.SecureRandom
instance and create a new one. This will generate a new instance with a new seed.java.security.SecureRandom.setSeed
(java.security.SecureRandom.generateSeed(int)
).In summary, keep the following in mind when using java.security.SecureRandom
:
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "SUN");
java.security.SecureRandom.nextBytes(byte[])
immediately after creating a new instance of the PRNG. This will force the PRNG to seed itself securely. If for testing purposes, you need predictable output, ignoring this rule and seeding the PRNG with hard-coded/predictable values may be appropriate.See other posts on SecureRandom
Issues to be aware of when using Java’s SecureRandom
SecureRandom Implementation (sun.security.provider.SecureRandom – SHA1PRNG)
SecureRandom Implementation (sun.security.provider.NativePRNG)
Amit Sethi is a principal consultant at Synopsys. He specializes in mobile security, online game security, and cryptography. Amit’s work includes extracting cryptographic keys from embedded devices using side-channel attacks, designing mechanisms to make those attacks more difficult, and designing a format-preserving encryption algorithm based on well-studied cryptographic primitives for a Fortune 500 company. Even in his free time, Amit enjoys reverse engineering binaries, analyzing open source software, and experimenting with new technologies.