Using cryptographically strong random number generator with SecureRandom in Java
There might be a need occasionally to generate sequences of random numbers in your real-world programs. While there is a special class in Java to deal just with that — java.util.Random
— it’s not cryptographically strong, and the numbers chosen are not completely random because a definite mathematical algorithm (based on Donald E. Knuth’s subtractive random number generator algorithm) is used to select them. Therefore it is not safe to use this class for tasks that require high level of security, like creating a random password, for example.
Fortunately, there’s another, much more cryptographically strong random number generator provided with every Java Runtime Environment by default. It can be accessed via the java.security.SecureRandom
class, which is a subclass of class Random
mentioned above. That means that you can use it the same way you did when you used the generator implemented by the Random
class, it even allows you to set the random seed of your choice if it happens so that you need to repeat the sequence of numbers generated, which is good as for example the .NET equivalent — System.Security.Cryptography.RNGCryptoServiceProvider
— does not allow to do that. However, there is one or two issues that, if not addressed, might turn into real problems and cause lots of headaches. But before I describe those, let me talk you into how to start using this strong random number generator.
While it is possible to construct an instance of the class directly like you used to do with “usual” generator: new SecureRandom()
, the SecureRandom
class defines series of getInstance
methods that allow you to obtain a new and fresh generator that uses the specified algorithm for generating random sequences. One of the methods takes single string argument and is sufficient for most situations where random numbers are needed. That argument specifies the name of the algorithm that will be used to generate random numbers. Currently, there is only one such algorithm provided by every Sun’s Java Runtime Environment by default, which uses SHA-1 as the foundation, and called "SHA1PRNG
" (for more information see Appendix A in the Java Cryptography Architecture API Specification & Reference). So, to construct new random number generator object all we need to do is call the getInstance(String)
method with "SHA1PRNG"
passed as parameter. The only unmentioned thing you have to remember about is the fact that getInstance
methods might throw an exception, and you have either to guard it with try/catch block, or add throws-declaration to the method containing a call to getInstance
. But, you are probably aware of all that already.
Random r;
try {
r = SecureRandom.getInstance("SHA1PRNG");
} catch(NoSuchAlgorithmException nsae) {
// Process the exception in some way or the other
}
After you have obtained a new instance of the random number generator, you can use it in just the usual way:
int i1 = r.nextInt(100); // Generate random Integer in the range [0 .. 100)
double d1 = r.nextDouble(); // Generate random Double in the range [0.0 .. 1.0)
boolean b1 = r.nextBoolean(); // Generate random Boolean value ("flip of a coin" - either true or false)
So far so good, but hang on, did I say anything about the unusual behavior? Yes, I did, and that behavior comes from the use of setSeed(long)
method.
First of all, passing zero to it does not change the state of the generator at all. This is different from what you’d expect when you call setSeed(0L)
on a usual Random
object, which can be seeded with zero value just fine, while SecureRandom
simply ignores such a call. There is an interesting reason for this (if you are not bothered about the reason, just skip on to the next paragraph). It is done to ignore the setSeed(0)
call from the constructor of Random
class, as at that point constructor of the SecureRandom
class hasn’t had chance to execute and so the fields declared in this class are not initialized yet, which means they cannot be operated from the overridden setSeed
method. Interestingly though, that Java allows to call overridden virtual methods right away, even before the class they are declared in is fully constructed (in some other programming languages, C++ for example, calling virtual method from the constructor of the super-class calls the method declared in that super-class rather than overridden one from the sub-class, this happens because virtual-tables get overwritten as constructors are executed down the hierarchy).
Other unusual behavior is that calling setSeed
(with non-zero value) does not actually reset the seed to the specified value. It rather alters the current seed using the new seed value. That may lead to a bit of a surprise when you want to reset the seed on a random number generator to a previous value so that the sequence of numbers could be repeated. Here, have a look at the following two code samples.
Random r = SecureRandom.getInstance("SHA1PRNG");
r.setSeed(1);
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print("| ");
r.setSeed(1);
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.println();
and
Random r = SecureRandom.getInstance("SHA1PRNG");
r.setSeed(1);
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print("| ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
These are a bit simplified examples in the sense that they do not explicitly deal with NoSuchAlgorithmException
, but at least they should show what happens when setSeed
method is called. Here are the outputs those examples produce.
Output of the first example, which contains setSeed
call after the first three integer numbers have been generated:
50 3 60 | 95 6 96 6 35 78
And this is output of the second example, which does not contain a call to the
setSeed
after the first three integer numbers have been generated:
50 3 60 | 95 6 11 76 30 28
The vertical bar (
|
) symbol denotes the place where the call to the setSeed
method is made (or is omitted). As you can see, the second call to the setSeed
method slightly modifies the sequence of numbers that are been generated. Not very much, but enough for two sequences to be different. Another observation that can be made is that the second call to the method did not reset the seed (this is just as I said earlier), i.e. the numbers that come out of the generator after the second call are not the same as the numbers that came out of it after the first call, despite fact the same argument was passed in both calls.If it ever happens that you do really need to reset the seed, the solution then is obvious: make the first call to the setSeed
method right after you obtain new instance of the random number generator from the getInstance
method, and when you need to reset the seed, get completely new instance of the random number generator again, and call setSeed
on that instance. Much like in this example:
Random r = SecureRandom.getInstance("SHA1PRNG");
// Make a call to setSeed right after a new instance of the generator has been obtained
r.setSeed(1);
// Generate a sequence of some random numbers
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print("| ");
// Obtain new instance of the random number generator before setting the seed
r = SecureRandom.getInstance("SHA1PRNG");
// Set exactly the same seed that was used for the first generator
r.setSeed(1);
// Generate a sequence of some random numbers. Note that the first three numbers
// in this sequence are exactly the same as numbers in the previous one
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.print(r.nextInt(100) + " ");
System.out.println();
This will output the following sequence of integers:
50 3 60 | 50 3 60 95 6 11
This concludes the post about the
SecureRandom
class and its use in Java lanaguage.
Shameless plug
We develop mobile and web apps, you can hire us to work on your next project.