SecureRandom in Java
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
| Point | java.util.Random | java.security.SecureRandom |
|---|---|---|
| Source | A simple formula started from a 48-bit seed | Random data gathered by the operating system |
| Same seed gives the same sequence | Yes | No, by default |
| Can future values be predicted from past ones? | Yes, from a couple of outputs | No |
| Speed | Very fast | Slower, but fast enough |
| Use for | Games, simulations, tests, sample data | Tokens, keys, salts, IVs, passwords, one-time codes |
Methods
| Method | Description |
|---|---|
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
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());
}
}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? trueExplanation:
Randomwith the same seed gives the same "random" number both times: 130. Anyone who learns or guesses the seed knows every value.token(32)fills 32 bytes fromSecureRandomand 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.- 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.
- A salt is 16 random bytes, used when hashing passwords.
- A 6-digit code:
nextInt(1_000_000)gives 0 to 999,999, and%06dkeeps leading zeros, so 42 is shown as000042. - 100,000 tokens were generated with no duplicates.
How Much Randomness?
| Purpose | Recommended size |
|---|---|
| Session ID, API key, reset token | At least 16 bytes (128 bits); 32 is common |
| Password salt | 16 bytes |
| AES key | 16 or 32 bytes |
| AES-GCM IV | 12 bytes |
| One-time code typed by a person | 6 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.
SecureRandomis thread-safe; astatic finalfield is the usual place. - Don't seed it yourself.
new SecureRandom()seeds itself from the operating system. CallingsetSeedwith a predictable value can only make things worse. - Encode bytes for text. Use Base64 (URL-safe for links) or hexadecimal.
UUID.randomUUID()usesSecureRandomand 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
- Easy: Generate a 20-byte token and print it in hexadecimal and in URL-safe Base64.
- Harder: Write a method that generates a 12-character password using
SecureRandom.nextIntover an alphabet of letters, digits and symbols.
Key Takeaways
Was this tutorial helpful?