AlikElzin-kilaka
AlikElzin-kilaka

Reputation: 36011

What's the best way to create a "good" SecureRandom?

There are a lot of questions asking if a specific initiation of SecureRandom is "good", but I couldn't find a rule of thumb.

What's the best way to create a "good" random SecureRandom?

// Fast
// Is it a good random?
SecureRandom secureRandom = new SecureRandom()?

// Freezes for a few seconds after being used several times - until getting a random enough seed.
// See http://stackoverflow.com/questions/137212/how-to-solve-performance-problem-with-java-securerandom#comment68934647_137212
// Is it a good random?
SecureRandom secureRandom = new SecureRandom(SecureRandom.getSeed(20))?

// Freezes for a very long time. Waited minutes and still no return :(
SecureRandom secureRandom = new SecureRandom.getInstanceStrong()?

Other?

Upvotes: 10

Views: 5268

Answers (1)

Maarten Bodewes
Maarten Bodewes

Reputation: 93998

Basically, the best answer is: you don't know. You'd better leave the choice up to the experts and use new SecureRandom(). This will retrieve the first random number generator by the highest priority provider that has one.

Which providers are present and which one has priority depends on the runtime (IBM and Android also have Java compatible runtimes). The runtime configuration may also differ per operating system, even for the standard JDK.

On an Virtual Machine you SHOULD install the OS specific client toolset of the particular VM manager; this commonly allows the client OS to seed from the host OS. SecureRandom commonly depends on the host to provide the seed or even the random data. If the host however cannot successfully seed itself then the Java runtime won't be able to either and the "random data" created on a virtual host may repeat.

From the JCA documentation:

All Java SE implementations provide a default SecureRandom using the no-argument constructor: new SecureRandom(). This constructor traverses the list of registered security providers, starting with the most preferred provider, then returns a new SecureRandom object from the first provider that supports a SecureRandom random number generator (RNG) algorithm. If none of the providers support a RNG algorithm, then it returns a SecureRandom object that uses SHA1PRNG from the SUN provider.


There is absolutely no need to "seed" the algorithm yourself. Calling getSeed() will try and retrieve a seed from the runtime. This may deplete the randomness pool, just like getInstanceStrong(), resulting in a block of your application and possibly other applications as well until entropy becomes available. The SecureRandom implementations will seed themselves - hopefully in the best way possible - if you don't provide a seed. Beware that the seed provided is mixed into the random pool in most (modern) implementations, which is initially seeded by e.g. the OS; you should not assume that two instances that have been provided the same seed will generate the same random sequence, not even during testing, and not even if you explicitly specify "SHA1PRNG". To be precise the deterministic behavior of "SHA1PRNG" is specific to the Sun / Oracle implementation and may also be dependent on the behavior of the runtime system.

If use of new SecureRandom() results in blocking then you need to make sure that your application doesn't use /dev/random directly, depleting the entropy pool. If it doesn't and it still blocks then /dev/random is probably misbehaving. Modern Linux kernels will basically make /dev/random and /dev/urandom identical; both will be using a deterministic RNG based on entropy gathered from the system and the CPU in case a hardware RNG is available.


For retrieving long term key key material you may also use SecureRandom.getInstanceStrong(). You should generally not use this though; SecureRandom should be strong enough for most use cases. If you use the getInstanceStrong() method you may deplete the entropy pool of your OS. Note that on newer systems this is less likely to happen. For instance on Linux both /dev/random and /dev/urandom point to the same pseudo-random number generators.


Preferably you should not use "SHA1PRNG". Even though all runtimes have an implementation, it is not an implementation requirement:

[1] No specific Configuration type, Policy type or SecureRandom algorithm is required; however, an implementation-specific default must be provided.

Note that Android first used an insecure "SHA1PRNG" implementation, which was subsequently replaced by OpenSSL native code. Basically the implementation lied on which RNG was used. The problem is that the algorithm of SHA1PRNG hasn't even been specified by Sun, so it is impossible to rely on any specifics of the algorithm, even if the default implementation seems to be secure.


NOTE 2024-03-02: this is still a fine answer IMHO, but it needs an update regarding the NIST compliant DRBG RNG's and the expanded API.

Upvotes: 9

Related Questions