How should you generate the salt for PBKDF2 in Java, and why does the choice of random source matter?
answer
- SecureRandom.nextBytes(new byte[16])
- Never java.util.Random / Math.random for salts
- Fresh salt per password, 16 bytes
- Salt is not secret - stored with the hash
- SecureRandom = CSPRNG, java.util.Random = predictable
basics
~20 sUse java.security.SecureRandom to fill a fresh byte array (16 bytes) for each password. Don't use java.util.Random or Math.random - they're predictable. The salt is stored with the hash and doesn't need to be secret, just unique and random.
solid answer
~40 sA salt is per-password random data that makes identical passwords hash differently and defeats precomputed rainbow tables. Generate it with java.security.SecureRandom, a cryptographically strong PRNG, by allocating a byte[] (16 bytes is standard) and calling nextBytes(salt). Never use java.util.Random or Math.random for this: they're fast but predictable PRNGs whose output can be reconstructed from a few samples, so an attacker could anticipate salts. Create a fresh salt for every password (and on every password change) - reusing one salt across users re-enables rainbow tables and reveals which users share a password. The salt is not secret; store it alongside the hash and iterations. On most platforms `new SecureRandom()` is fine; avoid forcing SHA1PRNG, and don't over-engineer with SecureRandom.getInstanceStrong() unless you specifically need a blocking strong source.
code
java · 10 linesimport java.security.SecureRandom;
// one shared, thread-safe instance is fine
private static final SecureRandom RNG = new SecureRandom();
byte[] newSalt() {
byte[] salt = new byte[16]; // 128 bits
RNG.nextBytes(salt);
return salt;
}go deeper
Uses SecureRandom.nextBytes for a 16-byte salt and knows not to use Math.random/java.util.Random.
Generates a fresh salt per password, stores it with the hash, and can explain rainbow-table defense.
Explains CSPRNG vs statistical PRNG, reuses a SecureRandom instance, sizes the salt, and knows when getInstanceStrong is and isn't appropriate.
Sets entropy/PRNG standards across services, considers blocking-entropy behavior in containers, and audits all random sources used for security material.
## What a salt is and why it exists A **salt** is a chunk of random bytes you mix into the hashing of each password. It solves two problems: 1. **Rainbow tables.** Attackers precompute huge tables mapping common passwords to their hashes. If everyone's password were hashed the same way with no salt, one table cracks everyone. A unique salt per password means the attacker would need a *separate* table per salt - infeasible. 2. **Identical passwords looking identical.** Without a salt, two users with the password `password123` get the *same* stored hash, leaking that fact. A per-user salt makes their stored hashes differ. Crucially, a salt is **not a secret**. It's stored in plaintext next to the hash. Its only requirements are: **unique per password** and **unpredictable/random**. ## Why the random *source* matters Computers generate "random" numbers with a **pseudo-random number generator (PRNG)** - an algorithm that produces a sequence from an internal state. There are two kinds: - **Statistical PRNGs** like `java.util.Random` (and `Math.random()`, which uses it). These are fast and "random enough" for games or sampling, but they're **predictable**: the algorithm is public and the state is small, so observing a little output lets an attacker compute past and future values. `java.util.Random` uses a 48-bit seed - tiny by crypto standards. - **Cryptographically secure PRNGs (CSPRNGs)** like **`java.security.SecureRandom`**. These are seeded from OS entropy (hardware noise, timing, etc.) and are designed so that even knowing lots of output gives no usable advantage in predicting more. For anything security-relevant - salts, tokens, keys, IVs, nonces - you **must** use a CSPRNG. If salts were predictable, an attacker could precompute against the likely salts, weakening the whole scheme. ## How to do it in Java ``` import java.security.SecureRandom; byte[] salt = new byte[16]; // 128 bits is standard new SecureRandom().nextBytes(salt); // fills with strong random bytes ``` Notes: - **Size:** 16 bytes (128 bits) is the common recommendation; bigger is fine, smaller risks collisions across many users. - **Freshness:** generate a new salt for *every* password and on *every* change. Never share one global salt. - **`new SecureRandom()`** is fine on modern JVMs/OSes - it picks a strong native provider. Avoid hardcoding the legacy `SHA1PRNG` algorithm. - **`SecureRandom.getInstanceStrong()`** returns a (possibly blocking) "strong" instance backed by a blocking entropy source on some systems; reserve it for long-lived keys, not high-volume salt generation where it can stall. - **Reuse the instance:** a single `SecureRandom` is thread-safe and seeding is the expensive part, so you can keep one around rather than constructing it in a tight loop. ## Storing the salt Because it's not secret, you store the salt right next to the hash and iteration count (e.g. base64 in a combined record). At verification you read it back and feed it into the `PBEKeySpec` to recompute the hash. ## The common mistakes - Using `Math.random()` / `new Random()` - predictable. - Reusing a single hardcoded salt for all users - re-enables rainbow tables and leaks shared passwords. - Deriving the salt from the username - it's then predictable and not unique on rename. - Making the salt too short (e.g. 4 bytes) - collisions across a large user base.
- Does the salt need to be kept secret like the password?No. A salt's job is to be unique and unpredictable-per-password, not hidden. It's stored in plaintext alongside the hash and iteration count. Security comes from the one-way hash and work factor, not from hiding the salt.
- Why is java.util.Random unsuitable for generating salts?It's a statistical PRNG with a small 48-bit seed and a public algorithm, so its output is predictable - an attacker can reconstruct the sequence from a few values. Predictable salts let attackers precompute attacks. SecureRandom is a CSPRNG seeded from OS entropy and is designed to be unpredictable.
saying these in an interview costs you the question
- Using Math.random() or java.util.Random to make a salt
- Reusing one global/hardcoded salt for all users
- Deriving the salt from the username or a counter
- Thinking the salt must be kept secret
- Using a 2-4 byte salt