3

Reading through the forum it seems the SecureRandom is thread safe, but it struggles in multi-threaded systems because of contention see Is SecureRandom thread safe? . Also initialising a new SecureRandomis an expensive operation. One suggestion to improve the performance is to use ThreadLocalRandom.

So I changed my code from:

SecureRandom randomSecureRandom = SecureRandom.getInstance("SHA1PRNG");

to:

private final ThreadLocalRandom randomThreadLocal = ThreadLocalRandom.current();

I did some tests - 100 runs for encrypting 10000 string values and I can see definite improvement.

**With ThreadLocalRandom**
Thread #18New encryption service took on average: 66.44ms.
Thread #17New encryption service took on average: 64.79ms.
Thread #14New encryption service took on average: 70.77ms.
Thread #13New encryption service took on average: 72.33ms.
Thread #19New encryption service took on average: 73.42ms.
Thread #15New encryption service took on average: 74.21ms.
Thread #11New encryption service took on average: 76.79ms.
Thread #16New encryption service took on average: 78.72ms.
Thread #12New encryption service took on average: 78.95ms.
Thread #20New encryption service took on average: 78.99ms.

**With SecureRandom**
Thread #19New encryption service took on average: 87.26ms.
Thread #18New encryption service took on average: 93.65ms.
Thread #13New encryption service took on average: 93.1ms.
Thread #15New encryption service took on average: 95.81ms.
Thread #16New encryption service took on average: 96.9ms.
Thread #11New encryption service took on average: 97.0ms.
Thread #20New encryption service took on average: 94.93ms.
Thread #17New encryption service took on average: 96.63ms.
Thread #12New encryption service took on average: 97.41ms.
Thread #14New encryption service took on average: 99.08ms.

It seems I definitely improved the speed here, however I degraded the security because it seems the ThreadLocalRandom is not cryptographically secure:

 * <p>Instances of {@code ThreadLocalRandom} are not cryptographically
 * secure.  Consider instead using {@link java.security.SecureRandom}
 * in security-sensitive applications

My question is - is there a way to create cryptographically secure Random number, which is thread safe and it's performing well in a multi-threaded system?

There is one more question touching this topic, however the answer is suggesting the same transition from SecureRandom -> ThreadLocalRandom, which is not cryptographically secure see Minimizing SecureRandom performance problems in multithreaded environment?.

Community
  • 1
  • 1
Anton Belev
  • 11,963
  • 22
  • 70
  • 111
  • 1
    How many threads need to use the random? I know you said that creating a `SecureRandom` is expensive, still, you may want to measure whether creating one per thread in the end gives you a faster result because there is no contention. – Ole V.V. Mar 13 '17 at 10:42
  • There are other ways of generating random values, i.e through the use of hardware, listening to a microphone's noise background or using an external magnetometer, one can also download random data from the internet however those methods are very unlikely to be faster and more effective in a multi-threaded environment. I suggest sticking with `SecureRandom` and making sure you reuse the same instance instance among multiple threads and just call the `.nextBytes(..)` function. In the end, if you want cryptographically strong values, you have to pay for it with performance. – cristianhh Mar 13 '17 at 10:52
  • So you saved roughly 30 ms per call. That's hardly a big change, is this going to matter in the overall performance? Do you want secure random numbers or just random numbers? – john16384 Mar 13 '17 at 11:20
  • @john16384 I will be using this random numbers for encryption so I guess for my need it's better to be secure random numbers. – Anton Belev Mar 13 '17 at 11:26
  • I would look at it another way - why not have one thread which is responsible purely for generating random data and putting it onto a blocking queue where other threads can take it from as needed. I would imagine that will give the best performance. – BarrySW19 Mar 13 '17 at 11:38
  • @BarrySW19 at peak I could be encrypting few million String values and having just single thread generating the random numbers does not seem to be optimal I guess. If it's possible to keep the multi-threaded solution but make it secure random would be the best. But looking at the discussion so far I will have to sacrifice one of the two. – Anton Belev Mar 13 '17 at 11:46
  • 1
    Well, I suppose the first thing to test is whether two threads actually generate numbers faster than one. If they do, then why not have two or more threads all busy keeping the queue buffer filled with random numbers? I can't see any other solution being faster. It shouldn't even be that hard to have the generator process add more threads as the queued data becomes low. – BarrySW19 Mar 13 '17 at 13:43

1 Answers1

3

SecureRandom implementation is much slower than ThreadLocalRandom. This is not related to thread safety.

ThreadLocalRandom's algorithm to generate next random number involves very few mathematical operations and is easy to crack. In fact, the result returned by a single nextLong operation is sufficient to determine all of the future and past numbers generated by that generator, see https://jazzy.id.au/2010/09/20/cracking_random_number_generators_part_1.html if you are interested in details.

On the other hand, SecureRandom with the provider you selected uses SHA1 to generate random numbers. SHA1 is computationally expensive, so the performance is worse than that of ThreadLocalRandom. This is by design - computational complexity is one of the factors that makes SHA1 hard to reverse, and the seed hard to guess.

To compare the performance I generated 100M random numbers in a single thread using both generators. ThreadLocalRandom took 95ms to complete, SecureRandom took 41 seconds.

EDIT to address threading performance:

You can create a SecureRandom instance for every thread. It will be initialized on first use with data taken from a static (shared / synchronized) instance, but subsequent operations will be thread-local. I measured the performance of 4 threads using shared vs dedicated SecureRandom instance. Each thread generated 100M random numbers; dedicated instance took 30 seconds, shared took 1 min 54 sec.

  • I think Joan Boyer was the first to publish how to break a LCG. Later, she showed how to break a LCG that discarded bits. Also see [Inferring Sequences Produced by Pseudo-Random Number Generator](http://asterix.cs.gsu.edu/crypto/p129-boyar.pdf). – jww Mar 15 '17 at 22:04