skip to content

What happens if you construct or seed SecureRandom with a fixed seed, and why is that dangerous?

level: middleimportance: must knowfreq 60%

answer

  1. Fixed seed -> deterministic, repeatable output
  2. Unpredictability comes only from OS entropy seeding
  3. SHA1PRNG + setSeed-before-use = fully determinized
  4. Default NativePRNG setSeed only ADDS entropy, doesn't replace
  5. Seeded RNG OK for tests, never in production

basics

~20 s

A fixed seed makes the output deterministic — the same 'random' values every run — which is fine for tests but disastrous for real secrets, because anyone with the seed (or who guesses it) can reproduce every token or key.

solid answer

~50 s

SecureRandom is still a deterministic algorithm; its unpredictability comes from being seeded with high-entropy OS data. If you supply a fixed seed — via new SecureRandom(seedBytes) on some algorithms, or by calling setSeed(constant) on a freshly created SHA1PRNG-style instance before any other use — you can pin the output so it repeats identically every run. That is sometimes done deliberately for reproducible tests, but in production it is a severe vulnerability: an attacker who knows or guesses the seed can regenerate every salt, IV, key, and token you produce. Two important nuances: setSeed semantics differ by algorithm — on the default native PRNG, setSeed only mixes additional entropy in rather than replacing it, so it won't fully determinize output — but SHA1PRNG seeded immediately after construction will. Never hardcode seeds in security code, and never derive a seed from a timestamp or other low-entropy value.

code

java · 21 lines
java
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

class SeedDanger {
    // DANGEROUS: SHA1PRNG seeded immediately -> identical output every run.
    static byte[] reproducibleAndBroken() throws NoSuchAlgorithmException {
        SecureRandom r = SecureRandom.getInstance("SHA1PRNG");
        r.setSeed("hardcoded-seed".getBytes()); // pins the sequence
        byte[] token = new byte[16];
        r.nextBytes(token); // same bytes on every JVM run
        return token;
    }

    // SAFE: let SecureRandom self-seed from OS entropy; never set a fixed seed.
    static byte[] safe() {
        SecureRandom r = new SecureRandom();
        byte[] token = new byte[16];
        r.nextBytes(token); // unpredictable
        return token;
    }
}

go deeper

for a junior

Understands that a fixed seed means the same values every run and that this must not be used for real secrets.

for a middle

Explains that unpredictability comes from OS entropy, that hardcoded/time-based seeds are dangerous, and that seeded RNGs are only for tests.

for a senior

Knows the algorithm-dependent setSeed semantics (SHA1PRNG replaces vs default supplements) and the concrete consequences like IV reuse breaking GCM/CTR.

for a principal

Sets policy banning fixed/low-entropy seeds, designs test seams that inject randomness without polluting production, and reviews for IV/salt reuse risks.

## Why a seed matters Every PRNG, including the cryptographically secure `SecureRandom`, is a **deterministic** algorithm: same starting state in, same sequence out. The only thing that makes `SecureRandom`'s output unpredictable is that it is **seeded from high-entropy operating-system randomness** that an attacker cannot observe. Remove or fix that seed and the 'randomness' becomes reproducible. ## How a fixed seed gets introduced There are two main ways developers accidentally (or intentionally) pin the output: 1. **Constructor with a seed:** `new SecureRandom(byte[] seed)`. On some algorithms this initializes the generator from exactly those bytes, so a constant seed → constant output. 2. **setSeed before first use:** Calling `secureRandom.setSeed(fixedBytes)` on a **freshly created** instance of an algorithm like **SHA1PRNG** uses that seed as the entire starting state, producing a fixed sequence. If the seed is a literal in code (e.g. `setSeed("12345".getBytes())`) or derived from something low-entropy (e.g. `setSeed(System.currentTimeMillis())`), the output is predictable. ## Why it's dangerous With a known/fixed seed, **every** secret you generate is reproducible by anyone who has (or guesses) the seed: - Salts repeat → password hashes become attackable / non-unique. - IVs repeat → encryption leaks patterns (catastrophic for modes like CTR/GCM where IV reuse breaks the cipher). - Tokens/keys repeat → forgery, account takeover, key compromise. A hardcoded seed is essentially shipping your secrets' recipe in source control. ## A crucial algorithm nuance: setSeed behavior differs `setSeed` does **not** mean the same thing for every algorithm: - For **SHA1PRNG**, calling `setSeed` right after construction (before any output) makes the seed the complete initial state → fully deterministic. - For the **default NativePRNG / DRBG**, `setSeed` typically **supplements** the existing OS-seeded state with additional entropy rather than replacing it. So `setSeed(constant)` there mixes in (harmless) extra bytes and does **not** make output reproducible. This is why a 'set a fixed seed to determinize SecureRandom' trick works on SHA1PRNG but may silently *not* work on the default — leading to confusing behavior. The safe rule ignores these subtleties: **never fix the seed in production**. ## Legitimate use: reproducible tests In unit tests you sometimes *want* deterministic 'random' values so assertions are stable. There it's acceptable to use a seeded SHA1PRNG (or better, inject a fake/stubbed source). Keep that strictly out of production code paths. ## Rules - Don't pass a fixed seed to the constructor or `setSeed` in production. - Don't seed from time, PID, counters, or any guessable value. - Let `SecureRandom` self-seed from the OS; you generally never need to call `setSeed` at all. - If you need determinism for tests, isolate it and make it obvious it's test-only.

  • Does setSeed always make SecureRandom deterministic?
    No. With SHA1PRNG called immediately after construction it does. With the default NativePRNG/DRBG, setSeed usually only mixes extra entropy into the already OS-seeded state, so output stays unpredictable.
  • When is a fixed seed acceptable?
    Only in tests that need reproducible 'random' values, kept strictly out of production code — and even then, injecting a fake randomness source is cleaner.

saying these in an interview costs you the question

  • Hardcoding a seed in production for 'consistency'
  • Seeding from System.currentTimeMillis()/nanoTime() (low entropy, guessable)
  • Assuming setSeed always determinizes output (algorithm-dependent)
  • Reusing IVs because a fixed seed reproduces them — breaks ciphers like GCM/CTR

context