Assuming that nobody's screwed up the implementation, it should not matter what kind of RNG you get. This is because all java.security.SecureRandom implementations are supposed to be cryptographically strong, as defined in RFC 1750 ยง6.3 (emphasis mine):
6.3 Cryptographically Strong Sequences
In cases where a series of random quantities must be generated, an
adversary may learn some values in the sequence. In general, they
should not be able to predict other values from the ones that they
know. […]
This includes the SHA1PRNG implementation, which is described as follows:
This algorithm uses SHA-1 as the foundation of the PRNG. It computes the SHA-1 hash over a true-random seed value concatenated with a 64-bit counter which is incremented by 1 for each operation. From the 160-bit SHA-1 output, only 64 bits are used.
Note two details here:
The internal state of the PRNG, which an attacker would need to learn in order to predict future output of the generator, consists of the random seed value and the counter. The output never contains these values, but only their SHA-1 hashes. In order to reconstruct the internal state from the output, an attacker would have to carry out a successful preimage attack on SHA-1, which is not currently believed to be feasible.
The output of the SHA-1 hash function is truncated to 64 bits, discarding a full 60% of it. Even if the internal state were propagated as in many non-cryptographic PRNGs, by repeatedly feeding the previous state through a mixing function (here, SHA-1), this truncation should be sufficient to prevent any reasonable attacker from reconstructing the full state, since they would effectively have to guess the remaining 96 bits of the state.
Thus, even though the output of SHA1PRNG is deterministic (given the initial seed, which the output never reveals), it should not be possible for any attacker, using currently known techniques and feasible computing resources, to predict its future output based on observed past outputs (or, indeed, even to distinguish its output from a truly random bitstream).
Now, for some caveats:
The security of SHA1PRNG relies entirely on the seed value being secure (i.e. secretly and randomly chosen from a sufficiently large set of probabilities to withstand brute force guessing attacks). If the generator is seeded improperly, all the security properties may be lost.
Normally, the first call to nextBytes()
should automatically seed the generator from a system-provided source of randomness. However, this automatic seeding does not happen if setSeed()
is called before the first call to nextBytes()
. In this case, the internal PRNG state depends entirely on the user-provided seed, which, if poorly chosen, might not have much entropy. (This is particularly prone to cause confusion because calling setSeed()
after the internal seeding just mixes the user-provided seed into the internal state, and so never reduces the randomness of the output.)
Also, while the automatic seeding is supposed to be secure, this might not always be the case in practice. Notably, a flaw in the PRNG seeding on Android was used in 2013 to steal bitcoins from people running bitcoin software on Android devices.
Even if the SecureRandom instance is used properly, the internal implementation of the PRNG might have bugs that compromise its security. Here's one paper I found that discusses weaknesses in various Java PRNG implementations.
If you don't trust the SHA1PRNG provided by your JRE, one option would be to explicitly request a NativePRNG implementation by using SecureRandom.getInstance("NativePRNG")
instead of new SecureRandom()
. Of course, this call may fail on systems that do not have such an implementation available, and in any case, still requires you to trust the OS native PRNG, and the Java interface to it provided by your Java crypto implementation.
The other alternative is to implement your own (deterministic) cryptographically secure pseudorandom number generator, such as HMAC_DRBG or CTR_DRBG from NIST SP 800-90A (note: do not use Dual_EC_DRBG!), and use it to generate your random numbers, possibly mixed (i.e. XORed) with the output of the OS native PRNG. The tricky part, here, is seeding the PRNG properly: if you have access to a secure OS RNG, you might as well use it directly; if not, you'd have to implement some ad hoc entropy collection scheme, which is difficult and easy to get wrong.