<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=windows-1252">
</head>
<body>
<p>Hi John,</p>
<p>As you may have noticed, we are progressing a fix for
<a class="moz-txt-link-freetext" href="https://bugs.openjdk.java.net/browse/JDK-8246613">https://bugs.openjdk.java.net/browse/JDK-8246613</a> for returning the
same default SecureRandom algo for 3rd party providers as in
pre-JDK7092821 releases. Thus, the chance of observing JDK-8246613
should be lowered significantly. Given this, I plan to lower the
priority of JDK-8246383 and it may not be fixed in JDK 15 as
earlier communicated. <br>
</p>
<p>If this will be an issue, please let me know.</p>
Thanks,<br>
Valerie<br>
<div class="moz-cite-prefix">On 6/2/2020 4:37 PM, Valerie Peng
wrote:<br>
</div>
<blockquote type="cite"
cite="mid:651b2416-4764-d460-e3f9-56ef073a5c35@oracle.com">
<p>Thanks for reporting the bug and the detailed analysis.</p>
<p>I have filed <a class="moz-txt-link-freetext"
href="https://bugs.openjdk.java.net/browse/JDK-8246383"
moz-do-not-send="true">https://bugs.openjdk.java.net/browse/JDK-8246383</a>
to keep track of this. Will aim to fix this for 15 and have it
backported accordingly.</p>
<p>Is it possible to get hold of an test provider to reproduce and
verifying the fix?<br>
</p>
<p>Regards,</p>
Valerie<br>
<div class="moz-cite-prefix">On 6/2/2020 1:18 PM, John Gray wrote:<br>
</div>
<blockquote type="cite"
cite="mid:DM6PR11MB25859D5591C9F26853DD7574EE8B0@DM6PR11MB2585.namprd11.prod.outlook.com">
<meta name="Generator" content="Microsoft Word 15 (filtered
medium)">
<div class="WordSection1">
<p class="MsoNormal">Hello,</p>
<p class="MsoNormal"> </p>
<p class="MsoNormal">At Entrust Datacard, we produce a Java
based toolkit that contains our own Security Provider.
This toolkit and provider has been around for about 19
years. </p>
<p class="MsoNormal"> </p>
<p class="MsoNormal">In JDK version 11.07 (and I also think
Java 12 and beyond), our toolkit reports the following
error:</p>
<p class="MsoNormal"><span>java.lang.RuntimeException:
java.security.NoSuchAlgorithmException: Error constructing
implementation (algorithm: X9_31usingAES256, provider:
Entrust, class:
com.entrust.toolkit.security.crypto.random.X9_31usingAES256)<br>
at
java.base/java.security.SecureRandom.getDefaultPRNG(SecureRandom.java:281)<br>
at
java.base/java.security.SecureRandom.<init>(SecureRandom.java:219)<br>
at
java.base/javax.crypto.JceSecurity.<clinit>(JceSecurity.java:80)<br>
... 41 more<br>
Caused by: java.security.NoSuchAlgorithmException: Error
constructing implementation (algorithm: X9_31usingAES256,
provider: Entrust, class:
com.entrust.toolkit.security.crypto.random.X9_31usingAES256)<br>
at
java.base/java.security.Provider$Service.newInstance(Provider.java:1825)<br>
at
java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:236)<br>
at
java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:164)<br>
at
java.base/java.security.SecureRandom.getInstance(SecureRandom.java:365)<br>
at
java.base/java.security.SecureRandom.getDefaultPRNG(SecureRandom.java:273)<br>
... 43 more<br>
Caused by: java.lang.NullPointerException<br>
<span>at
java.base/javax.crypto.JceSecurity.getVerificationResult(JceSecurity.java:203)</span><br>
at
java.base/javax.crypto.Cipher.getInstance(Cipher.java:690)<br>
at
java.base/javax.crypto.Cipher.getInstance(Cipher.java:625)<br>
at
com.entrust.toolkit.security.crypto.random.X9_31usingAES256.initialize(X9_31usingAES256.java:524)<br>
at
com.entrust.toolkit.security.crypto.random.X9_31usingAES256.<init>(X9_31usingAES256.java:102)<br>
at
java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native
Method)<br>
at
java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)<br>
at
java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)<br>
at
java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)<br>
at
java.base/java.security.Provider.newInstanceUtil(Provider.java:176)<br>
at
java.base/java.security.Provider$Service.newInstance(Provider.java:1818)</span></p>
<p class="MsoNormal"> </p>
<p class="MsoNormal">I investigated this error, and found it
was made possible because of the following change in Java
11.07 which unmasked a bug in the JVM that has probably been
around for years.</p>
<p class="MsoNormal"><a
href="https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8228613"
target="_blank" moz-do-not-send="true">https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8228613</a></p>
<p class="MsoNormal"> </p>
<p class="MsoNormal">It is a problem inside the JceSecurity
class. When the class is being loaded, the call to setup a
default SecureRandom() instance is invoked. That seems to
invoke the JVM to find the first available SecureRandom()
instance. This error happens when our Entrust provider is
in first position. In previous versions of the JDK it
honoured the order of algorithms specified in the
providers. In our Entrust Security provider, we have a
number of SecureRandom implementations. Now because of the
above change, it picks a different SecureRandom instance
(the X9_31usingAES256). That should be fine, however the
problem is that the SecureRandom() setup calls
Cipher.getInstance() and as you can see below, that calls
JceSecurity.getVerificationResult() which is static, and
uses the verificationResuts Map that has not yet been
initialized (becasuse it’s declaration is after the
SecureRandom setup). That is why there is a
NullPointerException.</p>
<p class="MsoNormal"> </p>
<p class="MsoNormal"><b><span>public</span></b><span> </span><b><span>static</span></b><span>
</span><b><span>final</span></b><span> Cipher
getInstance(String transformation,</span></p>
<p class="MsoNormal"><span>
Provider provider)</span></p>
<p class="MsoNormal"><span> </span><b><span>throws</span></b><span>
NoSuchAlgorithmException, NoSuchPaddingException</span></p>
<p class="MsoNormal"><span> {</span></p>
<p class="MsoNormal"><span> </span><b><span>if</span></b><span>
((transformation == </span><b><span>null</span></b><span>)
|| transformation.equals(</span><span>""</span><span>)) {</span></p>
<p class="MsoNormal"><span> </span><b><span>throw</span></b><span>
</span><b><span>new</span></b><span>
NoSuchAlgorithmException(</span><span>"Null or empty
transformation"</span><span>);</span></p>
<p class="MsoNormal"><span> }</span></p>
<p class="MsoNormal"><span> </span><b><span>if</span></b><span>
(provider == </span><b><span>null</span></b><span>) {</span></p>
<p class="MsoNormal"><span> </span><b><span>throw</span></b><span>
</span><b><span>new</span></b><span>
IllegalArgumentException(</span><span>"Missing provider"</span><span>);</span></p>
<p class="MsoNormal"><span> }</span></p>
<p class="MsoNormal"><span> Exception failure = </span><b><span>null</span></b><span>;</span></p>
<p class="MsoNormal"><span> List<Transform>
transforms = getTransforms(transformation);</span></p>
<p class="MsoNormal"><span> </span><b><span>boolean</span></b><span>
providerChecked = </span><b><span>false</span></b><span>;</span></p>
<p class="MsoNormal"><span> String paddingError = </span><b><span>null</span></b><span>;</span></p>
<p class="MsoNormal"><span> </span><b><span>for</span></b><span>
(Transform tr : transforms) {</span></p>
<p class="MsoNormal"><span> Service s =
provider.getService(</span><span>"Cipher"</span><span>,
tr.transform);</span></p>
<p class="MsoNormal"><span> </span><b><span>if</span></b><span>
(s == </span><b><span>null</span></b><span>) {</span></p>
<p class="MsoNormal"><span> </span><b><span>continue</span></b><span>;</span></p>
<p class="MsoNormal"><span> }</span></p>
<p class="MsoNormal"><span> </span><b><span>if</span></b><span>
(providerChecked == </span><b><span>false</span></b><span>)
{</span></p>
<p class="MsoNormal"><span> </span><span>//
for compatibility, first do the lookup and then verify</span><span></span></p>
<p class="MsoNormal"><span> </span><span>//
the provider. this makes the difference between a NSAE</span><span></span></p>
<p class="MsoNormal"><span> </span><span>//
and a SecurityException if the</span><span></span></p>
<p class="MsoNormal"><span> </span><span>//
provider does not support the algorithm.</span><span></span></p>
<p class="MsoNormal"><span> <span>Exception ve
= JceSecurity.getVerificationResult(provider);</span></span><span></span></p>
<p class="MsoNormal"><span> </span><b><span>if</span></b><span>
(ve != </span><b><span>null</span></b><span>) {</span></p>
<p class="MsoNormal"><span> String msg = </span><span>"JCE
cannot authenticate the provider "</span><span></span></p>
<p class="MsoNormal"><span> +
provider.getName();</span></p>
<p class="MsoNormal"><span> </span><b><span>throw</span></b><span>
</span><b><span>new</span></b><span> SecurityException(msg,
ve);</span></p>
<p class="MsoNormal"><span> }</span></p>
<p class="MsoNormal"><span> providerChecked = </span><b><span>true</span></b><span>;</span></p>
<p class="MsoNormal"><span> }</span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal"><span>The
JceSecurity.getVerificationResult(provider) method is used
when initializing the SecureRandom (first highlighted line
below) when the classLoader is loading the JceSecurity
class itself. </span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal"><span>From the JceSecurity class:</span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal"><b><span>static</span></b><span> </span><b><span>final</span></b><span>
SecureRandom RANDOM = </span><b><span>new</span></b><span>
SecureRandom();</span><span></span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal"><span> </span><span>// The
defaultPolicy and exemptPolicy will be set up</span><span></span></p>
<p class="MsoNormal"><span> </span><span>// in the static
initializer.</span><span></span></p>
<p class="MsoNormal"><span> </span><b><span>private</span></b><span>
</span><b><span>static</span></b><span> CryptoPermissions
defaultPolicy = </span><b><span>null</span></b><span>;</span></p>
<p class="MsoNormal"><span> </span><b><span>private</span></b><span>
</span><b><span>static</span></b><span> CryptoPermissions
exemptPolicy = </span><b><span>null</span></b><span>;</span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal"><span> </span><span>//
Map<Provider,?> of the providers we already have
verified</span><span></span></p>
<p class="MsoNormal"><span> </span><span>// value ==
PROVIDER_VERIFIED is successfully verified</span><span></span></p>
<p class="MsoNormal"><span> </span><span>// value is
failure cause Exception in error case</span><span></span></p>
<p class="MsoNormal"><span> </span><b><span>private</span></b><span>
</span><b><span>static</span></b><span> </span><b><span>final</span></b><span>
Map<Provider, Object> verificationResults =</span></p>
<p class="MsoNormal"><span> </span><b><span>new</span></b><span>
IdentityHashMap<>();</span><span></span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal"><span>It fails when it calls the
following code in JceSecurity.java because the
verificationResults Map<Provider, Object> has not
been initialized because the SecureRandom() constructor
ends up calling the JceSecurity.getVerificationResult()
static method that makes use of the Map! That explains
the NullPointerException. </span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal"><span>The fix to the issue should be
simple, just move the initialization of the
verificationResults Map BEFORE the SecureRandom
initialization in JceSecurity.java</span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal"><span>Because verificationResults is not
initialized, the line highlighted below fails (Because the
Map has not been initialized).</span></p>
<p class="MsoNormal"><span>/*</span><span></span></p>
<p class="MsoNormal"><span> * Verify that the provider JAR
files are signed properly, which</span><span></span></p>
<p class="MsoNormal"><span> * means the signer's
certificate can be traced back to a</span><span></span></p>
<p class="MsoNormal"><span> * JCE trusted CA.</span><span></span></p>
<p class="MsoNormal"><span> * Return null if ok, failure
Exception if verification failed.</span><span></span></p>
<p class="MsoNormal"><span> */</span><span></span></p>
<p class="MsoNormal"><span> </span><b><span>static</span></b><span>
</span><b><span>synchronized</span></b><span> Exception
getVerificationResult(Provider p) {</span></p>
<p class="MsoNormal"><span> <span>Object o =
verificationResults.get(p);</span></span><span></span></p>
<p class="MsoNormal"><span> </span><b><span>if</span></b><span>
(o == PROVIDER_VERIFIED) {</span></p>
<p class="MsoNormal"><span> </span><b><span>return</span></b><span>
</span><b><span>null</span></b><span>;</span></p>
<p class="MsoNormal"><span> } </span><b><span>else</span></b><span>
</span><b><span>if</span></b><span> (o != </span><b><span>null</span></b><span>)
{</span></p>
<p class="MsoNormal"><span> </span><b><span>return</span></b><span>
(Exception)o;</span></p>
<p class="MsoNormal"><span> }</span></p>
<p class="MsoNormal"><span> </span><b><span>if</span></b><span>
(verifyingProviders.get(p) != </span><b><span>null</span></b><span>)
{</span></p>
<p class="MsoNormal"><span> </span><span>// this
method is static synchronized, must be recursion</span><span></span></p>
<p class="MsoNormal"><span> </span><span>// return
failure now but do not save the result</span><span></span></p>
<p class="MsoNormal"><span> </span><b><span>return</span></b><span>
</span><b><span>new</span></b><span>
NoSuchProviderException(</span><span>"Recursion during
verification"</span><span>);</span></p>
<p class="MsoNormal"><span> }</span></p>
<p class="MsoNormal"><span> </span><b><span>try</span></b><span>
{</span></p>
<p class="MsoNormal"><span>
verifyingProviders.put(p, Boolean.FALSE);</span></p>
<p class="MsoNormal"><span> URL providerURL =
getCodeBase(p.getClass());</span></p>
<p class="MsoNormal"><span>
verifyProvider(providerURL, p);</span></p>
<p class="MsoNormal"><span> </span><span>//
Verified ok, cache result</span><span></span></p>
<p class="MsoNormal"><span>
verificationResults.put(p, PROVIDER_VERIFIED);</span></p>
<p class="MsoNormal"><span> </span><b><span>return</span></b><span>
</span><b><span>null</span></b><span>;</span></p>
<p class="MsoNormal"><span> } </span><b><span>catch</span></b><span>
(Exception e) {</span></p>
<p class="MsoNormal"><span>
verificationResults.put(p, e);</span></p>
<p class="MsoNormal"><span> </span><b><span>return</span></b><span>
e;</span></p>
<p class="MsoNormal"><span> } </span><b><span>finally</span></b><span>
{</span></p>
<p class="MsoNormal"><span>
verifyingProviders.remove(p);</span></p>
<p class="MsoNormal"><span> }</span></p>
<p class="MsoNormal"><span> }</span></p>
<p class="MsoNormal"> </p>
<p class="MsoNormal"><span>Cheers,</span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal"><span>John Gray</span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal"> </p>
<p class="MsoNormal"> </p>
</div>
</blockquote>
</blockquote>
</body>
</html>