1. Introduction
In this short tutorial, we’ll learn about java.security.SecureRandom, a class that provides a cryptographically strong random number generator.
2. Comparison to java.util.Random
Standard JDK implementations of java.util.Random use a Linear Congruential Generator (LCG) algorithm for providing random numbers. The problem with this algorithm is that it’s not cryptographically strong. In other words, the generated values are much more predictable, therefore attackers could use it to compromise our system.
To overcome this issue, we should use java.security.SecureRandom in any security decisions. It produces cryptographically strong random values by using a cryptographically strong pseudo-random number generator (CSPRNG).
For a better understanding of the difference between LCG and CSPRNG, please look at the below chart presenting a distribution of values for both algorithms:
3. Generating Random Values
The most common way of using SecureRandom is to generate int, long, float, double or boolean values:
int randomInt = secureRandom.nextInt(); long randomLong = secureRandom.nextLong(); float randomFloat = secureRandom.nextFloat(); double randomDouble = secureRandom.nextDouble(); boolean randomBoolean = secureRandom.nextBoolean();
For generating int values we can pass an upper bound as a parameter:
int randomInt = secureRandom.nextInt(upperBound);
In addition, we can generate a stream of values for int, double and long:
IntStream randomIntStream = secureRandom.ints(); LongStream randomLongStream = secureRandom.longs(); DoubleStream randomDoubleStream = secureRandom.doubles();
For all streams we can explicitly set the stream size:
IntStream intStream = secureRandom.ints(streamSize);
and the origin (inclusive) and bound (exclusive) values as well:
IntStream intStream = secureRandom.ints(streamSize, originValue, boundValue);
We can also generate a sequence of random bytes. The nextBytes() function takes user-supplied byte array and fills it with random bytes:
byte[] values = new byte[124]; secureRandom.nextBytes(values);
4. Choosing An Algorithm
By default, SecureRandom uses the SHA1PRNG algorithm to generate random values. We can explicitly make it use another algorithm by invoking the getInstance() method:
SecureRandom secureRandom = SecureRandom.getInstance("NativePRNG");
Creating SecureRandom with the new operator is equivalent to SecureRandom.getInstance(“SHA1PRNG”).
All random number generators available in Java can be found on the official docs page.
5. Seeds
Every instance of SecureRandom is created with an initial seed. It works as a base for providing random values and changes every time we generate a new value.
Using the new operator or calling SecureRandom.getInstance() will get the default seed from /dev/urandom.
We can change the seed by passing it as a constructor parameter:
byte[] seed = getSecureRandomSeed(); SecureRandom secureRandom = new SecureRandom(seed);
or by invoking a setter method on the already created object:
byte[] seed = getSecureRandomSeed(); secureRandom.setSeed(seed);
Remember that if we create two instances of SecureRandom with the same seed, and the same sequence of method calls is made for each, they will generate and return identical sequences of numbers.
6. Conclusion
In this tutorial, we’ve learned how the SecureRandom works and how to use it for generating random values.
As always, all code presented in this tutorial can be found over on GitHub.