skip to content

How do you use SecureRandom to fill a key, IV, salt, or token buffer, and what should you avoid when doing so?

level: middleimportance: must knowfreq 70%

answer

  1. nextBytes(buf) fills the array in place, returns void
  2. 16 bytes = 128-bit salt/IV; 32 bytes = 256-bit token
  3. Encode tokens with Base64 URL / hex, not new String(bytes)
  4. Reuse one SecureRandom (thread-safe)
  5. Range -> nextInt(bound), never byte % n

basics

~10 s

Create 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 s

To 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 lines
java
import 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

for a junior

Can call nextBytes on a sized byte[] to make a token and knows to use SecureRandom rather than Random.

for a middle

Knows nextBytes fills in place, picks sensible sizes (16/32 bytes), encodes tokens with Base64/hex, and reuses one instance.

for a senior

Avoids subtle pitfalls (modulo bias, new String on bytes, fixed seeds), reasons about entropy/bit-strength per use, and structures a reusable factory.

for a principal

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

context