SecureRandom safe seed in Java

21,277

Solution 1

No, you should avoid the SecureRandom(byte[]) constructor. It is both unsafe and non-portable.

It is non-portable because it behaves differently on Windows vs. other operating systems.

On most OSes, the default algorithm is "NativePRNG", which obtains random data from the OS (usually "/dev/random") and ignores the seed you provide.

On Windows, the default algorithm is "SHA1PRNG", which combines your seed with a counter and computes a hash of the result.

This is bad news in your example, because the input (the current UTC time in milliseconds) has a relatively small range of possible values. For example if an attacker knows that the RNG was seeded in the last 48 hours, they can narrow the seed down to less than 228 possible values, i.e. you have only 27 bits of entropy.

If on the other hand you had used the default SecureRandom() constructor on Windows, it would have called the native CryptoGenRandom function to get a 128-bit seed. So by specifying your own seed you have weakened the security.

If you really want to override the default seed (e.g. for unit testing) you should also specify the algorithm. E.g.

SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed("abcdefghijklmnop".getBytes("us-ascii"));

See also How to solve performance problem with Java SecureRandom?
and this blog post: http://www.cigital.com/justice-league-blog/2009/08/14/proper-use-of-javas-securerandom/

Solution 2

I think it is best to let the SecureRandom seed itself. This is done by calling nextBytes immediately after it's creation (calling setSeed will prevent this).

final byte[] dummy = new byte[512];
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.nextBytes(dummy);

You want to use SHA1PRNG because it guarantees a fast non-blocking implementation even on Linux, where the default is not.

Solution 3

The code is reasonably safe because it doesn't just use the seed given to seed the randomizer.

Its not much more random than just using.

SecureRandom randomizer = new SecureRandom();
Share:
21,277
Jordi P.S.
Author by

Jordi P.S.

Updated on October 26, 2020

Comments

  • Jordi P.S.
    Jordi P.S. over 3 years

    Is this piece of code safe?

     SecureRandom randomizer = new SecureRandom(String.valueOf(new Date().getTime()).getBytes());
    

    Is this the right way to instance the seed of secure random?

  • Hot Licks
    Hot Licks over 11 years
    Yep, probably using the default is better -- one assumes that the designers of the algorithm would pick a good seeding scheme. The scheme above probably weakens the seed slightly by converting to character representation.
  • user207421
    user207421 over 11 years
    It is considerably less random than using the default constructor.
  • Maarten Bodewes
    Maarten Bodewes over 11 years
    Note that the code you provide does initialize the pseudo random generator with just the encoded string as input. So it would create the same random each time. I think you meant to show exactly this, but you might want to make that more clear in the answer (as it is certainly not secure). It might also be different if any other provider would offer "SHA1PRNG", so in that regard it is a bit dangerous.
  • Prashant
    Prashant over 11 years
    If the nextBytes() call is intended for just kicking of the seeding, I would not use 512 byes but only 1 for efficency reasons.
  • Favolas
    Favolas over 10 years
    @PeterLawrey Here you recommend using 'new SecureRandom()' but at this you say to use 'System.nanoTime'. In this situation should I use 'new SecureRandom()' or 'System.nanoTime'. Thanks in advance
  • AlikElzin-kilaka
    AlikElzin-kilaka over 7 years
    @MaartenBodewes Why do you think the seed is ignored when using "NativePRNG"?
  • Saurabh
    Saurabh over 7 years
    @AlikElzin-kilaka it is not possible to use a user-provided seed with /dev/random. The native Windows RNG can, but when I posted this answer the JRE did not take advantage of this. That was 4 years ago though so it may have changed since then.