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.