How do you use SecureRandom to fill a key, IV, salt, or token buffer, and what should you avoid when doing so?
answer
- nextBytes(buf) fills the array in place, returns void
- 16 bytes = 128-bit salt/IV; 32 bytes = 256-bit token
- Encode tokens with Base64 URL / hex, not new String(bytes)
- Reuse one SecureRandom (thread-safe)
- Range -> nextInt(bound), never byte % n
basics
~10 sCreate one SecureRandom, make a byte[] of the size you need, and call secureRandom.nextBytes(buffer). That fills it with unpredictable bytes. Avoid building random bytes from Random, timestamps, or fixed seeds.
solid answer
~40 sTo generate random bytes for a salt, IV, key material, or token, allocate a byte[] of the required length and call SecureRandom.nextBytes(buffer); it fills the array in place with cryptographically strong random bytes. Reuse a single SecureRandom instance (it is thread-safe) rather than creating one per call, which can be wasteful. For a URL-safe token, encode the bytes with Base64 URL encoding (or hex) — don't try to map raw bytes to characters yourself. Size matters: salts/IVs are typically 16 bytes (128 bits); tokens usually 16–32 bytes. Never derive these bytes from java.util.Random, System.nanoTime(), or a fixed seed, and never call setSeed in a way that replaces the OS entropy. If you need an int in a range securely, use nextInt(bound) on SecureRandom rather than modulo on a raw byte.
code
java · 21 linesimport java.security.SecureRandom;
import java.util.Base64;
public final class TokenFactory {
// Thread-safe: create once, reuse everywhere.
private static final SecureRandom RNG = new SecureRandom();
/** 128-bit salt for password hashing. */
public static byte[] newSalt() {
byte[] salt = new byte[16];
RNG.nextBytes(salt); // fills in place
return salt;
}
/** URL-safe 256-bit token (e.g. password-reset link). */
public static String newToken() {
byte[] buf = new byte[32];
RNG.nextBytes(buf);
return Base64.getUrlEncoder().withoutPadding().encodeToString(buf);
}
}go deeper
Can call nextBytes on a sized byte[] to make a token and knows to use SecureRandom rather than Random.
Knows nextBytes fills in place, picks sensible sizes (16/32 bytes), encodes tokens with Base64/hex, and reuses one instance.
Avoids subtle pitfalls (modulo bias, new String on bytes, fixed seeds), reasons about entropy/bit-strength per use, and structures a reusable factory.
Defines org conventions for token/salt/IV generation and encoding, sets minimum entropy standards, and reviews crypto code for these pitfalls.
## What we're generating and why bytes Many cryptographic and security artifacts are just **random byte arrays**: - **Salt** — random bytes mixed into a password before hashing so identical passwords hash differently (defeats precomputed 'rainbow table' attacks). - **IV (initialization vector)** — random bytes that make each encryption of the same plaintext produce different ciphertext. - **Key material** — the secret bytes of a symmetric key. - **Token / nonce** — a one-off unguessable value (session id, reset token, request nonce). All of these must be **unpredictable**, so they must come from a CSPRNG: `java.security.SecureRandom`. ## The core API: nextBytes `SecureRandom` inherits from `java.util.Random` the method `void nextBytes(byte[] bytes)`. It does **not** return a value; it **fills the array you pass in**, in place, with random bytes. So the pattern is always: 1. Decide how many bytes you need (the *length* drives the security strength). 2. Allocate `byte[] buf = new byte[length];`. 3. Call `secureRandom.nextBytes(buf);`. 4. Use `buf` as the salt/IV/key, or encode it to text for a token. ### Sizing - 16 bytes = 128 bits is the common minimum for salts and IVs. - Tokens are typically 16–32 bytes (128–256 bits) — large enough that brute-forcing is infeasible. ### Turning bytes into a string token Raw random bytes aren't safe to drop into a URL or JSON. **Encode** them: - `Base64.getUrlEncoder().withoutPadding().encodeToString(buf)` for compact URL-safe text, or - hex via `HexFormat.of().formatHex(buf)`. Don't invent your own byte→char mapping (e.g. `new String(buf)`), which mangles bytes and loses entropy. ## Reuse the instance `SecureRandom` is **thread-safe**, so create one (e.g. a static final field) and reuse it. Constructing a new one each call is unnecessary and, depending on the algorithm/seeding, can cost extra. (Modern JDKs handle seeding efficiently, but reuse is still the clean default.) ## What to avoid - **Don't** build the bytes from `java.util.Random`, `Math.random()`, or `System.currentTimeMillis()/nanoTime()` — all predictable. - **Don't** call `setSeed(fixedValue)` to make output reproducible; that destroys unpredictability (see the determinism question). - **Don't** use `byte % n` to get a number in a range — it biases the distribution. Use `secureRandom.nextInt(bound)` instead. - **Don't** truncate to too few bytes to 'save space' — entropy is the whole point. ## Minimal example ```java private static final SecureRandom RNG = new SecureRandom(); byte[] salt = new byte[16]; // 128-bit salt RNG.nextBytes(salt); byte[] tokenBytes = new byte[32]; // 256-bit token RNG.nextBytes(tokenBytes); String token = Base64.getUrlEncoder().withoutPadding().encodeToString(tokenBytes); ```
- Does nextBytes return the array or fill it?It fills the array you pass in, in place, and returns void. You read the random bytes from the same array reference afterwards.
- How would you produce a URL-safe random token from the bytes?Base64-url-encode them: Base64.getUrlEncoder().withoutPadding().encodeToString(buf), or hex-encode with HexFormat. Don't construct a String directly from the raw bytes.
saying these in an interview costs you the question
- Using new String(randomBytes) as a token (mangles bytes, loses entropy)
- Generating a salt/IV from java.util.Random for 'speed'
- Using too few bytes (e.g. 4-byte token)
- byte % n for a ranged value (introduces bias)
- Calling setSeed with a constant to make output reproducible