Random numbers are an important part of software systems, especially those that deal with security or encryption. The Java platform offers different types of randomness based on intent, and static typing can further guide that choice for library developers.
This post is intended to explain how the Oracle JDK provides random numbers, to guide their usage:
Java SE provides three mechanisms for generating random numbers based on their usage:
Having these choices allows developers to make the appropriate decision based on their application.
The Java Platform also allows software operators to override and control this behavior. For example improving true-ness of the Random generator or improving speed of SecureRandom with dedicated hardware.
Java’s static typing can also enforce that program authors and library users apply the proper Random implementation. The random classes are designed with inheritance, allowing full interoperability across implementations. Both SecureRandom and ThreadLocalRandom inherit from Random, which means that implementations can be swapped or specified:
Random pseudoRandom = new Random();
Random pseudoRandomConcurrent = ThreadLocalRandom.current();
Random secureRandom = new SecureRandom();
For API design, developers can choose to only accept the appropriate version based on their use case. Given that true randomness is crucial to the success of cryptographic operations, it is critical that cryptographic APIs only accept SecureRandom arguments and never the super-class of Random. One example of this is KeyPairGenerator, which helps generate public/private keys that are used by many cryptographic systems. Users can only seed this with true-random values and not pseudo-random values.
KeyPairGenerator.initialize(int keysize, SecureRandom random);
Developers can only call this method by using statically-typed SecureRandom values. The super-type Random PRNG, which might make predictable keys, cannot work. Calling the overloaded KeyPairGenerator.initialize(int keysize) will also use a SecureRandom and is fine, the point here is that static typing prevents other types of Random from being used.
Using the previous example, one could call:
kpg.initialize(4096, (SecureRandom) secureRandom);
Or by specific static typing:
SecureRandom staticEnforcement = new SecureRandom();
API designers building secure systems should follow this example of statically typing SecureRandom in method signatures. When vetting secure software, look for usage of Random where a SecureRandom might be more appropriate. The US CERT Java coding guidelines offer additional guidance on evaluating the use of random numbers. Simon Kitching also has a good write-up about Using the SecureRandom Class.
The sources for randomness are listed in the JDK’s security Standard Names documentation, identifying which are blocking or non-blocking. Although some sources rely on entropy pools, SecureRandoms like SHA1PRNG are quite fast once seeded and do not need additional entropy.
Blocking is especially important on Linux systems, where some calls to /dev/random may pause while the system generates a new true-random number. There is a trade-off /dev/urandom device that is non-blocking but somewhat less random.
Within a Java installation, the administrator can adjust various settings through the "JRE/lib/security/java.security" configuration file. That file contains documentation about configuring securerandom.source and securerandom.strongAlgorithms. On Linux systems, the administrator may set their securerandom.source to /dev/random or /dev/urandom accordingly.
Each operating system contains its recommended default value. System Administrators may edit that value, for example if their system has different RNGs or if they have a separate specialized hardware RNG.
JavaMex has a page describing the sources of entropy that Java uses on different operating systems.
Additional information is available in the SecureRandom javadoc.