NullPointer in JceSecurity.getVerificationResult - Affects JDK 11.07, and JDK 12 and later.

Valerie Peng valerie.peng at oracle.com
Wed Jun 10 20:13:32 UTC 2020


Hi John,

As you may have noticed, we are progressing a fix for 
https://bugs.openjdk.java.net/browse/JDK-8246613 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.

If this will be an issue, please let me know.

Thanks,
Valerie
On 6/2/2020 4:37 PM, Valerie Peng wrote:
>
> Thanks for reporting the bug and the detailed analysis.
>
> I have filed https://bugs.openjdk.java.net/browse/JDK-8246383 to keep 
> track of this. Will aim to fix this for 15 and have it backported 
> accordingly.
>
> Is it possible to get hold of an test provider to reproduce and 
> verifying the fix?
>
> Regards,
>
> Valerie
> On 6/2/2020 1:18 PM, John Gray wrote:
>>
>> Hello,
>>
>> 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.
>>
>> In JDK version 11.07 (and I also think Java 12 and beyond), our 
>> toolkit reports the following error:
>>
>> java.lang.RuntimeException: java.security.NoSuchAlgorithmException: 
>> Error constructing implementation (algorithm: X9_31usingAES256, 
>> provider: Entrust, class: 
>> com.entrust.toolkit.security.crypto.random.X9_31usingAES256)
>> at 
>> java.base/java.security.SecureRandom.getDefaultPRNG(SecureRandom.java:281)
>> at java.base/java.security.SecureRandom.<init>(SecureRandom.java:219)
>> at java.base/javax.crypto.JceSecurity.<clinit>(JceSecurity.java:80)
>> ... 41 more
>> Caused by: java.security.NoSuchAlgorithmException: Error constructing 
>> implementation (algorithm: X9_31usingAES256, provider: Entrust, 
>> class: com.entrust.toolkit.security.crypto.random.X9_31usingAES256)
>> at 
>> java.base/java.security.Provider$Service.newInstance(Provider.java:1825)
>> at 
>> java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:236)
>> at 
>> java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:164)
>> at 
>> java.base/java.security.SecureRandom.getInstance(SecureRandom.java:365)
>> at 
>> java.base/java.security.SecureRandom.getDefaultPRNG(SecureRandom.java:273)
>> ... 43 more
>> Caused by: java.lang.NullPointerException
>> at 
>> java.base/javax.crypto.JceSecurity.getVerificationResult(JceSecurity.java:203)
>> at java.base/javax.crypto.Cipher.getInstance(Cipher.java:690)
>> at java.base/javax.crypto.Cipher.getInstance(Cipher.java:625)
>> at 
>> com.entrust.toolkit.security.crypto.random.X9_31usingAES256.initialize(X9_31usingAES256.java:524)
>> at 
>> com.entrust.toolkit.security.crypto.random.X9_31usingAES256.<init>(X9_31usingAES256.java:102)
>> at 
>> java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native 
>> Method)
>> at 
>> java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
>> at 
>> java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
>> at 
>> java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
>> at java.base/java.security.Provider.newInstanceUtil(Provider.java:176)
>> at 
>> java.base/java.security.Provider$Service.newInstance(Provider.java:1818)
>>
>> 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.
>>
>> https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8228613
>>
>> 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.
>>
>> *public**static**final*Cipher getInstance(String transformation,
>>
>> Provider provider)
>>
>> *throws*NoSuchAlgorithmException, NoSuchPaddingException
>>
>>     {
>>
>> *if*((transformation == *null*) || transformation.equals("")) {
>>
>> *throw**new*NoSuchAlgorithmException("Null or empty transformation");
>>
>>         }
>>
>> *if*(provider == *null*) {
>>
>> *throw**new*IllegalArgumentException("Missing provider");
>>
>>         }
>>
>>         Exception failure = *null*;
>>
>>         List<Transform> transforms = getTransforms(transformation);
>>
>> *boolean*providerChecked = *false*;
>>
>>         String paddingError = *null*;
>>
>> *for*(Transform tr : transforms) {
>>
>>             Service s = provider.getService("Cipher", tr.transform);
>>
>> *if*(s == *null*) {
>>
>> *continue*;
>>
>>             }
>>
>> *if*(providerChecked == *false*) {
>>
>> // for compatibility, first do the lookup and then verify
>>
>> // the provider. this makes the difference between a NSAE
>>
>> // and a SecurityException if the
>>
>> // provider does not support the algorithm.
>>
>> Exception ve = JceSecurity.getVerificationResult(provider);
>>
>> *if*(ve != *null*) {
>>
>>                     String msg = "JCE cannot authenticate the provider "
>>
>>                         + provider.getName();
>>
>> *throw**new*SecurityException(msg, ve);
>>
>>                 }
>>
>>                 providerChecked = *true*;
>>
>>             }
>>
>> The JceSecurity.getVerificationResult(provider) method is used when 
>> initializing the SecureRandom (first highlighted line below) when the 
>> classLoader is loading the JceSecurity class itself.
>>
>> From the JceSecurity class:
>>
>> *static**final*SecureRandom RANDOM = *new*SecureRandom();
>>
>> // The defaultPolicy and exemptPolicy will be set up
>>
>> // in the static initializer.
>>
>> *private**static*CryptoPermissions defaultPolicy = *null*;
>>
>> *private**static*CryptoPermissions exemptPolicy = *null*;
>>
>> // Map<Provider,?> of the providers we already have verified
>>
>> // value == PROVIDER_VERIFIED is successfully verified
>>
>> // value is failure cause Exception in error case
>>
>> *private**static**final*Map<Provider, Object> verificationResults =
>>
>> *new*IdentityHashMap<>();
>>
>> 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.
>>
>> The fix to the issue should be simple, just move the initialization 
>> of the verificationResults Map BEFORE the SecureRandom initialization 
>> in JceSecurity.java
>>
>> Because verificationResults is not initialized, the line highlighted 
>> below fails (Because the Map has not been initialized).
>>
>> /*
>>
>>      * Verify that the provider JAR files are signed properly, which
>>
>>      * means the signer's certificate can be traced back to a
>>
>>      * JCE trusted CA.
>>
>>      * Return null if ok, failure Exception if verification failed.
>>
>>      */
>>
>> *static**synchronized*Exception getVerificationResult(Provider p) {
>>
>> Object o = verificationResults.get(p);
>>
>> *if*(o == PROVIDER_VERIFIED) {
>>
>> *return**null*;
>>
>>         } *else**if*(o != *null*) {
>>
>> *return*(Exception)o;
>>
>>         }
>>
>> *if*(verifyingProviders.get(p) != *null*) {
>>
>> // this method is static synchronized, must be recursion
>>
>> // return failure now but do not save the result
>>
>> *return**new*NoSuchProviderException("Recursion during verification");
>>
>>         }
>>
>> *try*{
>>
>> verifyingProviders.put(p, Boolean.FALSE);
>>
>>             URL providerURL = getCodeBase(p.getClass());
>>
>> verifyProvider(providerURL, p);
>>
>> // Verified ok, cache result
>>
>> verificationResults.put(p, PROVIDER_VERIFIED);
>>
>> *return**null*;
>>
>>         } *catch*(Exception e) {
>>
>> verificationResults.put(p, e);
>>
>> *return*e;
>>
>>         } *finally*{
>>
>> verifyingProviders.remove(p);
>>
>>         }
>>
>>     }
>>
>> Cheers,
>>
>> John Gray
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/security-dev/attachments/20200610/1f3deb91/attachment.htm>


More information about the security-dev mailing list