JDK-8266929 : Unable to use algorithms from 3p providers
  • Type: Bug
  • Component: security-libs
  • Sub-Component: java.security
  • Affected Version: openjdk8u292,11.0.11
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2021-05-11
  • Updated: 2022-01-31
  • Resolved: 2021-05-25
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 Other
11.0.12-oracleFixed openjdk8u302Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Opening on behalf of Dan Lutker <lutkerd@amazon.com>

The change for “JDK-8249906, CVE-2021-2163: Enhance opening JARs” exposes a problem with the caching of algorithms beyond what is being reported in JDK-8266290 or what is fixed by in JDK-8242565. Not only are the SunJCE algorithms no longer available when using signed JARs in 8u292, but no algorithms can be added via new providers in either JDK 8 or JDK 11 The caching of the oidTable happens only once which is now done when JARs are loaded, when you add a new provider this table is never refreshed. Executing the simple reproducer UseBCAlgo.java (requires BouncyCastle) on 8u282 or 11.0.10 works successfully, but both 8u292 and 11.0.11 fail with “java.security.NoSuchAlgorithmException: unrecognized algorithm name: SHA384WITHDSA”.
 
Exception in thread "main" java.security.NoSuchAlgorithmException: unrecognized algorithm name: SHA384WITHDSA
                at java.base/sun.security.x509.AlgorithmId.get(AlgorithmId.java:448)
                at java.base/javax.crypto.EncryptedPrivateKeyInfo.<init>(EncryptedPrivateKeyInfo.java:139)
                at UseBCAlgo.main(UseBCAlgo.java:8)
 
The caching is also incorrect when you remove a provider after it was successfully loaded, if a provider is removed the algorithms it provides are still available.
 
Proposed fix is to clear out the AlgorithmId oidTable whenever signed jar verification completes or the Security providers change, which would cover the regression introduced by JDK-8249906 as well as these issues so that the available Algorithms will reflect the currently loaded providers. This fix is an addition to JDK-8156584 and JDK-8242565, it does not supersede them and it will not work without JDK-8156584.
Comments
URL: https://hg.openjdk.java.net/jdk-updates/jdk11u/rev/40d1e784e193 User: goetz Date: 2021-05-26 12:21:50 +0000
26-05-2021

Fix Request (OpenJDK 8u): Please approve this regression fix which affects OpenJDK 8u292 too. Patch is the same as JDK 11u (after unshuffeling). While it adds more than necessary static mapping of OIDs to AlgorightmIDs it doesn't seem to be a good enough reason to diverge from JDK 11. Algorithms supported by jarsigner are different in JDK 8, but all the needed algorithms supported by the sigalg option[1] are supported Passes the reproducer after the patch. [1] SHA1withDSA, SHA256withRSA, or SHA256withECDSA are used if no sigalg option is being specified. https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jarsigner.html
25-05-2021

URL: https://hg.openjdk.java.net/jdk-updates/jdk11u-dev/rev/40d1e784e193 User: sgehwolf Date: 2021-05-25 14:46:42 +0000
25-05-2021

Fix Request (OpenJDK 11u): Please approve this 11u specific fix. JDK-8249906, an April CPU CVE fix, introduced a regression where verifying certain signed jar files triggered initialization of AlgorithmId's OID cache which then is never refreshed. That results in exceptions being thrown in certain situations when third party security providers are in play. The fix delays AlgorightmID's OID cache initialization until after jar file verification (for most cases), which gets us back to pre 11.0.11 behaviour. Patch has been reviewed by Dan Lutker and Paul Hohensee. Testing: jdk_security. No new regressions. webrev: https://cr.openjdk.java.net/~sgehwolf/webrevs/JDK-8266929/jdk11/02/webrev/
25-05-2021

Will do. Thanks, Sean!
19-05-2021

[~sgehwolf] You should propose the fix, and you can use that patch, thank you.
19-05-2021

[~mullan] Do you want to propose this fix to jdk-updates-dev for review or shall I?
19-05-2021

So I had a closer look as to why this isn't an issue in JDK 17 and 16. As Sean said, it's JDK-8242151 basically. For one, JDK-8242151 has "SHA256WithRSA" in KnownOIDs.java thus aliasOidsTable() is never called when jar signatures are being verified. For two it also adds "SHA384withDSA" to KnownOIDs.java: https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/sun/security/util/KnownOIDs.java#L157 Thus, the code goes through KnownOIDs.findMatch() and succeeds, never reaching the aliasOidsTable() method call in method AlgorithmId.algOID(String). So for JDK 16 and 17 aliasOidsTable() is never even called, let alone early on at jar file verification time. This is different in JDK 11 and 8. When I add some debug trace code like so: diff --git a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java --- a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java +++ b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java @@ -606,6 +606,7 @@ return AlgorithmId.sha512WithECDSA_oid; } + System.out.println("name = " + name); return oidTable().get(name.toUpperCase(Locale.ENGLISH)); } @@ -633,8 +634,11 @@ /** Collects the algorithm names from the installed providers. */ private static HashMap<String,ObjectIdentifier> computeOidTable() throws IOException { + System.out.println("computeOidTable()"); + Thread.dumpStack(); HashMap<String,ObjectIdentifier> tab = new HashMap<>(); for (Provider provider : Security.getProviders()) { + System.out.println("provider name = " + provider.getName()); for (Object key : provider.keySet()) { String alias = (String)key; String upperCaseAlias = alias.toUpperCase(Locale.ENGLISH); ... and run the UseBCAlgo.java then I see this: name = SHA256withRSA computeOidTable() java.lang.Exception: Stack trace at java.base/java.lang.Thread.dumpStack(Thread.java:1383) at java.base/sun.security.x509.AlgorithmId.computeOidTable(AlgorithmId.java:638) at java.base/sun.security.x509.AlgorithmId.oidTable(AlgorithmId.java:628) at java.base/sun.security.x509.AlgorithmId.algOID(AlgorithmId.java:610) at java.base/sun.security.x509.AlgorithmId.get(AlgorithmId.java:441) at java.base/sun.security.pkcs.SignerInfo.verify(SignerInfo.java:380) at java.base/sun.security.pkcs.PKCS7.verify(PKCS7.java:578) at java.base/sun.security.pkcs.PKCS7.verify(PKCS7.java:595) at java.base/sun.security.pkcs.SignerInfo.getTimestamp(SignerInfo.java:545) at java.base/sun.security.pkcs.SignerInfo.verify(SignerInfo.java:318) at java.base/sun.security.pkcs.PKCS7.verify(PKCS7.java:578) at java.base/sun.security.pkcs.PKCS7.verify(PKCS7.java:595) at java.base/sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:283) at java.base/sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:259) at java.base/java.util.jar.JarVerifier.processEntry(JarVerifier.java:316) at java.base/java.util.jar.JarVerifier.update(JarVerifier.java:230) at java.base/java.util.jar.JarFile.initializeVerifier(JarFile.java:759) at java.base/java.util.jar.JarFile.ensureInitialization(JarFile.java:1038) at java.base/java.util.jar.JavaUtilJarAccessImpl.ensureInitialization(JavaUtilJarAccessImpl.java:69) at java.base/jdk.internal.loader.URLClassPath$JarLoader$2.getManifest(URLClassPath.java:870) at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:786) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:698) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:621) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:579) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) at java.base/java.lang.Class.getDeclaredMethods0(Native Method) at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3166) at java.base/java.lang.Class.getMethodsRecursive(Class.java:3307) at java.base/java.lang.Class.getMethod0(Class.java:3293) at java.base/java.lang.Class.getMethod(Class.java:2106) at java.base/sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:816) at java.base/sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:675) provider name = SUN provider name = SunRsaSign provider name = SunEC provider name = SunJCE usePreCheck = false name = SHA384WITHDSA Exception in thread "main" java.security.NoSuchAlgorithmException: unrecognized algorithm name: SHA384WITHDSA at java.base/sun.security.x509.AlgorithmId.get(AlgorithmId.java:448) at java.base/javax.crypto.EncryptedPrivateKeyInfo.<init>(EncryptedPrivateKeyInfo.java:139) at UseBCAlgo.main(UseBCAlgo.java:23) So Sean's proposed fix makes sense to me. By making "SHA256WithRSA" known to AlgorithmId we avoid early initialization of the cache table. After patch I see this: $ java -cp bcprov-jdk15on-168.jar:. UseBCAlgo usePreCheck = false name = SHA384WITHDSA computeOidTable() java.lang.Exception: Stack trace at java.base/java.lang.Thread.dumpStack(Thread.java:1383) at java.base/sun.security.x509.AlgorithmId.computeOidTable(AlgorithmId.java:641) at java.base/sun.security.x509.AlgorithmId.oidTable(AlgorithmId.java:631) at java.base/sun.security.x509.AlgorithmId.algOID(AlgorithmId.java:613) at java.base/sun.security.x509.AlgorithmId.get(AlgorithmId.java:441) at java.base/javax.crypto.EncryptedPrivateKeyInfo.<init>(EncryptedPrivateKeyInfo.java:139) at UseBCAlgo.main(UseBCAlgo.java:23) provider name = SUN provider name = SunRsaSign provider name = SunEC provider name = SunJSSE provider name = SunJCE provider name = SunJGSS provider name = SunSASL provider name = XMLDSig provider name = SunPCSC provider name = JdkLDAP provider name = JdkSASL provider name = SunPKCS11 provider name = BC Successfully created third-party provider algo. GOOD.
19-05-2021

[~mullan] I've filed JDK-8267397 for the AID refresh issue. As far as I can see it's been a latent bug (no regression).
19-05-2021

FWIW, the reproducer should really use a signature algo which is in neither JDK version and only in a third party provider. SHA384WITHDSA is available in JDK 15+ it seems. "GOST3411WITHECGOST3410" from BC provider would work.
19-05-2021

A potentially simpler fix is the following: ``` --- a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java +++ b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java @@ -584,6 +584,9 @@ public class AlgorithmId implements Serializable, DerEncoder { || name.equalsIgnoreCase("SHA1/RSA")) { return AlgorithmId.sha1WithRSAEncryption_oid; } + if (name.equalsIgnoreCase("SHA256WithRSA")) { + return AlgorithmId.sha256WithRSAEncryption_oid; + } if (name.equalsIgnoreCase("SHA1withECDSA") || name.equalsIgnoreCase("ECDSA")) { return AlgorithmId.sha1WithECDSA_oid; ``` This registers the SHA256withRSA OID, which the JDK providers are typically signed with. This won't fallback to searching through the registered providers' OID mappings until later, when it is necessary after the 3rd party provider has been added. This doesn't completely address the issue as the OID cache can become stale in other ways (as you note the cache is not repopulated after removing a provider), but I believe that is a pre-existing issue, and not a regression. I think it would be better to fix that issue in the mainline for further review and bake time.
18-05-2021

This looks to be indeed caused by JDK-8249906. Reproducer passes with 11.0.11+8 (EA) and 8u292-b09 (EA) which both don't have that patch.
18-05-2021

Why must EPKI forget the cached OIDs? AlgorithmId is independent from any provider. Update: I was wrong.
18-05-2021

EncryptedPrivateKeyInfo(String, byte[]) throws NoSuchAlgorithmException "if the specified algName is not supported." The spec could be a bit clearer, but I interpret this that if all of the installed providers do not support the algorithm, it should throw this exception.
18-05-2021

The JDK mainline doesn't throw the NoSuchAlgorithmException, this is due to the AlgorithmId code being significantly restructured in JDK 15, see https://bugs.openjdk.java.net/browse/JDK-8242151. However, the call to EPKI doesn't fail after you remove the provider. This is because AlgorithmId remembers the cached OIDs even if the provider is removed. It's not clear at what point that regressed or if it ever behaved correctly - more code digging is needed. But it doesn't appear to be behavior that was changed by JDK-8249906. I would file a separate issue for that, if necessary. But for now, I am marking this with jdk17-na so we know that finding the 3rd-party provider OIDs isn't an issue. Also, if you call Signature.getInstance("SHA384WITHDSA") it does throw an exception after the provider has been removed.
17-05-2021

Isn't this an issue with JDK head too?
17-05-2021