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. This seed works as a base for generating random values and changes every time a new value is generated.
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);
We should remember that setting the seed for SecureRandom doesn’t guarantee the same random number generations because it combines the provided seed with multiple sources of entropy for enhanced unpredictability. In fact, in contrast to other pseudorandom number generators, it’s designed to be non-deterministic, even when seeded, to maintain cryptographic security.
6. Conclusion
In this article, we’ve learned how SecureRandom works and how to use it to generate random values.
As always, the source code for the examples is available over on GitHub.