SecureRandom Slow Initialization

Suppose you are doing a simple thing:

public class Main { public static void main(String[] args) { long started = System.currentTimeMillis(); try { new URL(args[0]).openConnection(); } catch (Exception ignore) { } System.out.println(System.currentTimeMillis() - started); } } 

Now run it with http: // localhost as args[0]

~100 msec is required to complete.

Now try https: // localhost

5000+ msec .

Now run the same on linux or in docker:

  • http: ~100 msec
  • https: ~350 msec

Why is this? Why is there such a huge difference between platforms? What can you do about it?

For long-term application and application servers with their own long and heavy initialization sequence, these 5 seconds may not matter.

However, there are many applications where this initial 5sec freezes and matters and can be disappointing ...

+8
java performance windows random
source share
1 answer

Explanation

The reason for this is the default SecureRandom provider.

There are 2 SecureRandom providers available on SecureRandom :

 - provider=SUN, type=SecureRandom, algorithm=SHA1PRNG - provider=SunMSCAPI, type=SecureRandom, algorithm=Windows-PRNG 

On Linux (tested in alpine docker with Oracle JDK 8u162):

 - provider=SUN, type=SecureRandom, algorithm=NativePRNG - provider=SUN, type=SecureRandom, algorithm=SHA1PRNG - provider=SUN, type=SecureRandom, algorithm=NativePRNGBlocking - provider=SUN, type=SecureRandom, algorithm=NativePRNGNonBlocking 

They are listed in the jre/lib/security/java.security .

 security.provider.1=sun.security.provider.Sun ... security.provider.10=sun.security.mscapi.SunMSCAPI 

By default, the first SecureRandom provider is SecureRandom . On Windows, sun.security.provider.Sun used by sun.security.provider.Sun , and this implementation reports that when starting the JVM with -Djava.security.debug="provider,engine=SecureRandom" :

 Provider: SecureRandom.SHA1PRNG algorithm from: SUN provider: Failed to use operating system seed generator: java.io.IOException: Required native CryptoAPI features not available on this machine provider: Using default threaded seed generator 

And the default seed generator is very slow by default.

You need to use the SunMSCAPI provider.

Solution 1: Configuration

Register suppliers in configuration:

Edit jre/lib/security/java.security :

 security.provider.1=sun.security.mscapi.SunMSCAPI ... security.provider.10=sun.security.provider.Sun 

I do not know that this can be done through the properties of the system.

Or maybe yes using -Djava.security.properties (untested, see this )

Solution 2: software

Programmatically program suppliers:

 Optional.ofNullable(Security.getProvider("SunMSCAPI")).ifPresent(p->{ Security.removeProvider(p.getName()); Security.insertProviderAt(p, 1); }); 

Now the JVM reports the following ( -Djava.security.debug="provider,engine=SecureRandom" ):

 Provider: SecureRandom.Windows-PRNG algorithm from: SunMSCAPI 

Solution 3: Programmatic v2

Inspired by this idea , only one SecureRandom service is inserted after the code SecureRandom , configured dynamically from the existing SunMSCAPI provider without explicit dependency on sun.* Classes. It also avoids the potential risks of indiscriminately prioritizing all SunMSCAPI provider SunMSCAPI .

 public interface WindowsPRNG { static void init() { String provider = "SunMSCAPI"; // original provider String type = "SecureRandom"; // service type String alg = "Windows-PRNG"; // algorithm String name = String.format("%s.%s", provider, type); // our provider name if (Security.getProvider(name) != null) return; // already registered Optional.ofNullable(Security.getProvider(provider)) // only on Windows .ifPresent(p-> Optional.ofNullable(p.getService(type, alg)) // should exist but who knows? .ifPresent(svc-> Security.insertProviderAt( // insert our provider with single SecureRandom service new Provider(name, p.getVersion(), null) {{ //NOSONAR setProperty(String.format("%s.%s", type, alg), svc.getClassName()); }}, 1))); } } 

Performance

<140 msec (instead of 5000+ msec )

More details

There is a call to new SecureRandom() somewhere down the call stack when you use URL.openConnection("https://...")

It calls getPrngAlgorithm() (see SecureRandom: 880 )

And this returns the found SecureRandom provider SecureRandom .

For testing purposes, calling URL.openConnection() can be replaced with the following:

 new SecureRandom().generateSeed(20); 

Renouncement

I am not aware of any negative side effects caused by supplier reordering. However, there may be some, especially given the default provider selection algorithm.

In any case, at least theoretically, from a functional point of view, this should be transparent to the application.

+10
source share

All Articles