The fastest way to generate random numbers in Java (safely!)
Explore Java's Random and SecureRandom class differences and learn to generate secure, high-entropy random numbers for cryptography or sensitive applications.
Nov 25, 2024 • 3 Minute Read
In this article, we’ll talk about a few of the different ways Java creates randomness. Whether you are a Java developer or know other languages, this article will help you understand a key observation: Not all randomness is… well… random.
Free Will
Some people argue about whether or not humans have free will. The ability for GMail to predict what my next few words will be says “perhaps not”, while my four-year-old screaming “Dad, dad! Watch this!” and then doing the most random thing I’ve ever seen seems to say otherwise.
Generally, though, people aren’t very good at randomness. Even when we think we are being quite unpredictable, our actions can at least be predicted at scale or en masse. How many of you thought you were being random “enough” at the beginning of the Internet when you added a ! to the end of your password?
Levels of Randomness
I don’t mention that to make anyone feel bad or get defensive. It’s a bit of a false dichotomy anyway since randomness is neither present nor absent but instead is quantifiable.
That quantity is called entropy, or, given the last set of outputs, how easy is it for me to predict the next set of outputs?
For example, if I start typing out the characters p-a-s-s-w- you can probably predict with a high degree of certainty that I will type an o next. But, if I type #-g-l-*-z-, you’ll be much less certain of what will come next.
java.util.Random Is A Mere Mortal
The simplest way to generate randomness in Java is java.util.Random. You can use this class to generate a random number like so:
Random rnd = new Random();
long nextLong = rnd.nextLong();
Because a long in Java is 8 bytes, there are over 9 quintillion possible numbers that could be returned from nextLong on any given call. If every number has an equal chance of coming out (or in other words, if Random were perfectly random), then Random would offer 128 bits of entropy.
But, that’s not the case. At best, Random offers only 48 bits of entropy. (And don’t fall for the illusion that it’s 1 - 48/128 = 62.5% worse; the correct math is 1 - (2^48/2^128) = 99.999…% worse!).
And its JavaDoc tells us so:
/**
* … used to generate a stream of pseudorandom numbers …
*/
For this reason, you can use this for things like testing, fuzzing, or creating random variations on non-sensitive information.
java.util.SecureRandom Is the Hero We Need
But for all other purposes in Java, you should use SecureRandom. You use it in the same way:
SecureRandom rnd = new SecureRandom();
long nextLong = rnd.nextLong();
The API is the same, but the level of randomness is different, enough to be relied upon for sensitive information, secure runtimes, and cryptographic keys.
The reason that it is different from Random is the underlying algorithm, which offers up to 128 bits of randomness.
Its JavaDoc says something different to reflect that:
/**
* … provides a cryptographically strong random number generator …
*/
Friends Don’t Let Friends Use java.util.Random
So, if SecureRandom is so much better, why keep both around? The main reason is throughput; SecureRandom can be 30-50 times slower.
Don’t let that fool you though. If you can’t afford the value being guessed in advance, which in the end is most cases, take the hit and stick with SecureRandom.
Conclusion
In this article, you learned about the two APIs available to you in Java: Random and SecureRandom. You learned a bit about entropy (and free will!) and that in most scenarios we want the extra security and unpredictability that SecureRandom provides. And whether it’s Java or some other language, make sure to consider how random your random number generator is before using it.