The Java SecureRandom class

The SecureRandom class, housed in the java.security package, provides a drop-in replacement to java.lang.Random. But unlike the latter, java.security.SecureRandom is designed to be cryptographically secure. We'll look in more detail below at what this means. First, let's address the question from a simple, practical point of view: when should you use SecureRandom?

When to use SecureRandom

SecureRandom is typically used in cases where:

On the other hand, it may not be an appropriate generator where CPU time need to generate numbers is a factor that is worth trading for quality of randomness. As a rough guide:

SecureRandom is about twenty to thirty times slower than "good medium-quality" generators such as the XORShift or the Numerical Recipes combined generator.

CPU usage may be an issue, for example, in:

Properties of SecureRandom

We said that SecureRandom is designed to be cryptographically secure. In practice, this means that the generator has the following properties:

These properties are important in various security applications. The first is important, for eaxmple, if we use the generator to produce, say, a session ID on a web server: we don't want user n to predict user n+1's session ID. Similarly, we don't want a user in an Internet cafe, based on the session ID or encryption key that they are given to access a web site, to be able to predict the value assigned to a previous user on that machine.

The other properties are together important when picking, say, a session ID or encryption key, to ensure that any of all the possibly values (or a sufficient proportion of them) can be selected, and we'll take a moment now to consider why.

The importance of producing "all values with equal probability"

For example, let's say that we want to pick a 128-bit AES encryption key. The idea of a strong encryption algorithm such as AES is that in order for an adversary to guess the key by "brute force" (which we assume is the only possible means), they would have to try every single possible key in turn until they hit on the right one. By law of averages, we would expect them to find it after half as many guesses as there are possible keys. A 128-bit key has 2128 possible values, so on average, they would have to try 2127 keys. In decimal 2127 is a 39-digit number. Or put another way, trying a million million keys per second, it would take 5x1015 millennia to try 2127 keys. Not even the British government wants to decrypt your party invitations that badly. So with current mainstream technology1, a 128-bit key is in principle sufficient for most applications.

But these metrics hold true only if our key selection algorithm— i.e. our random number generator— genuinely can pick any of the possible keys. For example, we certainly should not choose the key as follows:

// This is WRONG!! Do not do this!
Random ranGen = new Random();
byte[] aesKey = new byte[16]; // 16 bytes = 128 bits
ranGen.nextBytes(aesKey);

The problem here is that the period of java.util.Random is only 248. Even though we're generating a 128-bit key, we will only ever pick from a subset of 248 of the possible keys. Or put another way: an attacker need only try on average 247 keys, and will find our key by trial and error in a couple of days if they try just a thousand million keys per second. And as if that wasn't bad enough, they probably don't even need to try anywhere near 247: for reasons discussed earlier, there's a good chance that an instance of java.util.Random created within a couple of minutes of bootup will actually be seeded from a about one thousandth of the 248 possible values. This time, HM Sniffing Service doesn't even need expensive hardware to find the secret location of your housewarming party: a trip to Staples will give them all the computing power they need.

So as you've probably guessed, our solution to the problem is to use SecureRandom instead:

import java.security.SecureRandom;
..
Random ranGen = new SecureRandom();
byte[] aesKey = new byte[16]; // 16 bytes = 128 bits
ranGen.nextBytes(aesKey);

Now, there's a good chance that any of the 2128 possible keys will be chosen.

Seeding of SecureRandom

In order to provide this property of choosing any seed with "equal" likelihood, (or at least, with no bias that is practically detectable), SecureRandom seeds itself from sources of entropy available from the local machine, such as timings of I/O events.


1. Note that with a quantum computer, the complexity of guessing the key is apparently proportional to the square root of the number of possible keys. Then, guessing the 128-bit key at the same rate would take only about 200 days.


If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.

Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.