Java provides java.util.Random class for random number generation. However, this class should not be used to generate random numbers in applications where security is critical. For example, Synchronizer Toker is often used to mitigate CSRF attacks. This token should be generated using a high quality Random Number Generator (RNG) so that the attacker is not able to predict the next token. Even while generating encryption key or SESSIONID for highly secure applications, the next key or session Id of next user should be unpredictable for the attacker.
Random (and its subclass ThreadLocalRandom which is introduced in Java 7) has a period of 48 bits. For a brute force attack to generate it by law of averages would take 247 attempts. Using Random to generate random numbers of length greater than 48 bits would increase the predictability of the number. For example, to generate a random number of 128 bits, if Random is used, then it would actually be using a period of 48 bits under the hood to generate random numbers. Such a 128 bit random number won’t be equally distributed over its range.
Moreover, there is a bug 6955840 that is fixed in Java 7, which did not set the seed value while using Random(seed) constructor. The bug 7051516 was found in ThreadLocalRandom that is now fixed in Java 7 update 2, which generated same sequence of random numbers as the seed value was not initialized.
The FIPS 140-2 Security Requirements for Cryptographic Modules specifies random number generator tests which should be passed by a cryptographically strong random number. A cryptographically strong random number should also adhere to RFC 1750 Randomness Recommendations for Security.
Hence for generating random numbers which are secure, unpredictable and evenly distributed the following pseudo random number generators (PRNGs) can be used:
The SecureRandom which is a subclass of Random can be used to generate cryptographically strong random number. It is not itself a PRNG. It uses underlying PRNGs which are operating system default or the one specified during its creation. On Windows, the SHA1PRNG algorithm is used and it is implemented by sun.security.provider.SecureRandom. On Solaris/Linux, the sun.security.provider.NativePRNG is called which uses /dev/urandom to generate the random number.
SHA1 algorithm has a period of 160 bits. By law of averages, it would take an attacker 2159 attempts to forge the random number which is nearly impossible.
Listing 1 demonstrates a way to generate 20 byte random number using SecureRandom. Here, the operating system default will be used to generate the random number.
SecureRandom randomGenerator = new SecureRandom(); byte randomNumber = new byte; randomGenerator.nextBytes(randomNumber);
Listing 1. Generating random number using OS default PRNG algorithm
SecureRandom randomGenerator = SecureRandom.getInstance(“SHA1PRNG”); byte randomNumber = new byte; randomGenerator.nextBytes(randomNumber);
Listing 2. Generating random number using the SHA1PRNG algorithm
SecureRandom randomGenerator = SecureRandom.getInstance(“SHA1PRNG”,”SUN”); byte randomNumber = new byte; randomGenerator.nextBytes(randomNumber);
Listing 3. Generating random number by specifying algorithm and provider
When the SecureRandom instance is created, it is unseeded. The seed value is set during the first call to
nextBytes(). The seed value is generated from sources of entropy.
The seed value can be set using the
setSeed() method. The SecureRandom can also be used to generate the seed value as shown in Listing 4. When the seed value is explicitly set the internal seeding mechanism is bypassed. Care should be taken while setting the seed value as it may compomise the security. Usually predictable seed values such as timestamp should be avoided.
SecureRandom randomGenerator = … //generate 10 byte seed byte seed = randomGenerator.generateSeed(10); randomGenerator.setSeed(seed);
Listing 4. Use SecureRandom to generate and set seed value
To minimize the possibility of brute force attack, the SecureRandom should be periodically reseeded.
SecureRandom is 20 to 30 times slower than Random. Hence, it should be used only when you need a high-quality secure random number. Other random number generators can be used to generate faster and medium-quality random numbers.
Generating MAC using SecureRandom
A Message Authentication Code (MAC) can be generated by passing the random number generated by SecureRandom to MessageDigest as shown in Listing 5. The java.security.MessageDigest class provides message digest algorithms such as MD5 or SHA.
SecureRandom randomGenerator = SecureRandom.getInstance(“SHA1PRNG”); byte randomNumber = new byte; randomGenerator.nextBytes(randomNumber); MessageDigest messageDigest = MessageDigest.getInstance(“SHA-1”); messageDigest.update(randomNumber); byte mac = messageDigest.digest();
Listing 5. Generating Message Authentication Code (MAC) using SecureRandom
Java 5 introduced the UUID class to generate immutable universally unique identifier. It has a period of 128 bits. Four types of UUIDs can be generated using this class – time-based, DCE security, name-based and randomly generated UUID. The UUID type can be determined from its version. The static randomUUID() method as shown in Listing 6 generates the randomly generated UUID (type 4).
UUID randomUUID = UUID.randomUUID(); String randomNumber = randomUUID.toString();
Listing 6. Generate randomly generate UUID
This Java library provides the following PRNGs:
- MersenneTwisterRNG – This PRNG is faster than Random.
- CellularAutomationRNG – This is the fastest PRNG.
- AESCounterRNG – This is used for generating cryptographically strong random number. This class is 10 times faster than SecureRandom but should not be used for highly sensitive applications.
Most of the Uncommon Maths PRNGs pass the Diehard test suite.