SecureRandom in Java

Naveen AnamaneniUpdated · 6 min read

SecureRandom is Java's generator of cryptographically strong random numbers: values that are unpredictable, even to someone who has seen earlier outputs. Security depends on that. A session token, a password-reset link, an encryption key or a salt is only safe if an attacker can't guess it. The ordinary java.util.Random is fast and fine for games and simulations, but its output can be predicted from a few values, so it must never be used for security. SecureRandom extends Random, so it has the same methods; what changes is where the randomness comes from. In this lesson, you will learn the difference and how to generate tokens, salts and one-time codes.

Random vs SecureRandom

Pointjava.util.Randomjava.security.SecureRandom
SourceA simple formula started from a 48-bit seedRandom data gathered by the operating system
Same seed gives the same sequenceYesNo, by default
Can future values be predicted from past ones?Yes, from a couple of outputsNo
SpeedVery fastSlower, but fast enough
Use forGames, simulations, tests, sample dataTokens, keys, salts, IVs, passwords, one-time codes

Methods

MethodDescription
new SecureRandom()It is used to create a generator with the platform's default strong source.
nextBytes(byteArray)It fills an array with random bytes. This is the most used method.
nextInt(bound)It returns a random int from 0 up to, but not including, bound.
nextLong(), nextDouble(), nextBoolean()They return other random values.
SecureRandom.getInstanceStrong()It returns the strongest generator available; it may wait for the system to gather randomness.

Example: Tokens, Salts and Codes

Randoms.java▶ Try it
import java.security.SecureRandom;
import java.util.Base64;
import java.util.HashSet;
import java.util.HexFormat;
import java.util.Random;
import java.util.Set;
 
public class Randoms {
    static final SecureRandom SECURE = new SecureRandom();
 
    static String token(int bytes) {
        byte[] buffer = new byte[bytes];
        SECURE.nextBytes(buffer);
        return Base64.getUrlEncoder().withoutPadding()
            .encodeToString(buffer);
    }
 
    public static void main(String[] args) {
        // java.util.Random with the same seed repeats exactly.
        Random a = new Random(42);
        Random b = new Random(42);
        System.out.println("Random(42) twice: " + a.nextInt(1000) + " and "
            + b.nextInt(1000));
 
        // SecureRandom: unpredictable, so we can only describe the results.
        String t1 = token(32);
        String t2 = token(32);
        System.out.println("Token length: " + t1.length()
            + " characters from 32 random bytes");
        System.out.println("Two tokens equal? " + t1.equals(t2));
 
        byte[] salt = new byte[16];
        SECURE.nextBytes(salt);
        System.out.println("Salt: " + HexFormat.of().formatHex(salt).length()
            + " hex characters");
 
        int otp = SECURE.nextInt(1_000_000);
        System.out.println("6-digit code in range? "
            + (otp >= 0 && otp < 1_000_000) + ", shown as "
            + String.format("%06d", otp).length() + " digits");
 
        Set<String> seen = new HashSet<>();
        for (int i = 0; i < 100_000; i++) {
            seen.add(token(16));
        }
        System.out.println("100,000 tokens, all different? "
            + (seen.size() == 100_000));
        System.out.println("Algorithm available? "
            + !SECURE.getAlgorithm().isEmpty());
    }
}
Output
Random(42) twice: 130 and 130
Token length: 43 characters from 32 random bytes
Two tokens equal? false
Salt: 32 hex characters
6-digit code in range? true, shown as 6 digits
100,000 tokens, all different? true
Algorithm available? true

Explanation:

  1. Random with the same seed gives the same "random" number both times: 130. Anyone who learns or guesses the seed knows every value.
  2. token(32) fills 32 bytes from SecureRandom and encodes them as URL-safe Base64, giving 43 characters. That is 256 bits of randomness, suitable for a session ID or a password-reset link.
  3. Two tokens are never equal in practice, and the values can't be shown here because they differ on every run. That unpredictability is the whole point.
  4. A salt is 16 random bytes, used when hashing passwords.
  5. A 6-digit code: nextInt(1_000_000) gives 0 to 999,999, and %06d keeps leading zeros, so 42 is shown as 000042.
  6. 100,000 tokens were generated with no duplicates.

How Much Randomness?

PurposeRecommended size
Session ID, API key, reset tokenAt least 16 bytes (128 bits); 32 is common
Password salt16 bytes
AES key16 or 32 bytes
AES-GCM IV12 bytes
One-time code typed by a person6 digits, valid for a few minutes, with a limit on attempts

A 6-digit code has only a million possibilities. It is safe only because it expires quickly and guesses are limited.

Using It Well

  • Create one instance and reuse it. SecureRandom is thread-safe; a static final field is the usual place.
  • Don't seed it yourself. new SecureRandom() seeds itself from the operating system. Calling setSeed with a predictable value can only make things worse.
  • Encode bytes for text. Use Base64 (URL-safe for links) or hexadecimal.
  • UUID.randomUUID() uses SecureRandom and gives 122 random bits. It is good for unique IDs; for secrets, a dedicated token of 128 bits or more is the clearer choice.

Common Mistakes

Using Random or Math.random() for tokens or passwords. They are predictable.

Seeding with the current time. An attacker can guess the time.

Tokens that are too short. Eight random characters can be searched; use at least 128 bits.

Creating a new SecureRandom for every value. It works, but wastes time; share one.

Interview Corner

What is the difference between Random and SecureRandom?

java.util.Random is a linear congruential generator with a 48-bit seed: it is fast and reproducible, and its future outputs can be predicted after observing a few values, so it is unsuitable for security. java.security.SecureRandom is a cryptographically strong generator seeded from operating system entropy; its output is unpredictable. Use SecureRandom for keys, IVs, salts, session identifiers, tokens and one-time codes, and Random for simulations, games and tests.

Practice

  1. Easy: Generate a 20-byte token and print it in hexadecimal and in URL-safe Base64.
  2. Harder: Write a method that generates a 12-character password using SecureRandom.nextInt over an alphabet of letters, digits and symbols.

Quick check: SecureRandom

4 questions
1Why is java.util.Random unsafe for tokens?
2Which method fills an array with random bytes?
3Should you seed a SecureRandom with the current time?
4How many random bytes are recommended for a session token?

Key Takeaways

Was this tutorial helpful?

Lesson 511 of 578