When using SecureRandom, it is important not to use predictable seeds. This class is used to generate cryptographically strong random numbers. Using a predictable seed will make its output predictable as well, which counteracts the use case of SecureRandom.

Why is this an issue?

java.security.SecureRandom is often used to generate random values for cryptographic algorithms. When a random number generator is used for cryptographic purposes, the generated numbers must be as random and unpredictable as possible. When SecureRandom is improperly seeded with a constant or a predictable value, its output will also be predictable.

This can have severe security implications for cryptographic operations that rely on the randomness of the generated numbers. By using a predictable seed, an attacker can potentially guess or deduce the generated numbers, compromising the security of whatever cryptographic algorithm relies on SecureRandom.

What is the potential impact?

It is crucial to understand that the strength of cryptographic algorithms heavily relies on the quality of the random numbers used. By improperly seeding the SecureRandom class, we introduce a significant weakness that can be exploited by attackers.

Insecure cryptographic keys

One of the primary use cases for the SecureRandom class is generating cryptographic keys. If an attacker can predict the seed used to initialize the SecureRandom instance, they may be able to derive the same keys. Depending on the use case, this can lead to multiple severe outcomes, such as:

Session hijacking and man-in-the-middle attack

Another scenario where this vulnerability can be exploited is in the generation of session tokens or nonces for secure communication protocols. If an attacker can predict the seed used to generate these tokens, they can impersonate legitimate users or intercept sensitive information.

How to fix it in Java SE

Code examples

The following code uses a cryptographically strong random number generator to generate data that is not cryptographically strong.

Noncompliant code example

import java.security.SecureRandom

val sr = SecureRandom()
sr.setSeed(123456L) // Noncompliant
val v = sr.nextInt()
import java.security.SecureRandom

val sr = SecureRandom("abcdefghijklmnop".toByteArray(charset("us-ascii"))) // Noncompliant
val v = sr.nextInt()

Compliant solution

import java.security.SecureRandom

val sr = SecureRandom()
val v = sr.nextInt()

This solution is available for JDK 1.8 and higher.

import java.security.SecureRandom

val sr = SecureRandom.getInstanceStrong()
val v = sr.nextInt()

How does this work?

When the randomly generated data needs to be cryptographically strong, SecureRandom is the correct class to use. However, its documentation also cites that "any seed material passed to a SecureRandom object must be unpredictable". When no seed is passed by the user to the object, the SecureRandom object chooses an unpredictable seed by default. Therefore, the easiest way to fix the issue is to use the default constructor without any calls to SecureObject.setSeed().

To go the extra mile, SecureObject.getInstanceStrong() returns an instance of SecureObject that is guaranteed to use a strong algorithm for its number generation.

If the randomly generated data is not used for cryptographic purposes and is not business critical, it may be a better choice to use java.util.Random instead. In this case, setting a predictable seed may be acceptable depending on the situation.

Resources

Documentation

Standards