Thanks Valerie!

If I understand you correctly, I think you are saying the fix for 
https://bugs.openjdk.java.net/browse/JDK-8246613  will mean the first 
SecureRandom in the provider will be used as the default (which will make it 
equivalent to JDK 11.06 and previous versions).    If that is the case, I agree 
it should mitigate the issue and would be ok with lowering the priority of 
8246383.

Will this new fix be back-ported into 11 LTS versions as well?

Thanks so much for your amazing fast response.   I have been trying to set 
aside some time to put together a simple sample you can use to verify 8246383 
(that doesn't require our full toolkit), but haven't been able to find the time 
to do that yet.

Cheers,

John Gray



From: Valerie Peng [mailto:valerie.p...@oracle.com]
Sent: Wednesday, June 10, 2020 4:14 PM
To: John Gray <john.g...@entrustdatacard.com>; security-dev@openjdk.java.net
Cc: Muthu Kannappan <mu...@entrustdatacard.com>; Raj Arora 
<raj.ar...@entrustdatacard.com>
Subject: [EXTERNAL]Re: NullPointer in JceSecurity.getVerificationResult - 
Affects JDK 11.07, and JDK 12 and later.

WARNING: This email originated outside of Entrust Datacard.
DO NOT CLICK links or attachments unless you trust the sender and know the 
content is safe.
________________________________

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




Reply via email to