JDK-8266290 : Jar Verification performs initializations that cause NoSuchAlgorithmException
  • Type: Bug
  • Component: security-libs
  • Sub-Component: java.security
  • Affected Version: openjdk8u292
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2021-04-28
  • Updated: 2021-05-19
  • Resolved: 2021-05-18
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
During class loading from a signed Jar an initialization of the sun.security.x509.AlgorithmId class may happen (via sun.security.pkcs.SignerInfo#verify(sun.security.pkcs.PKCS7, byte[]) lines 375ff) but at that point only a fraction of the Providers are actually being returned from Security.getProviders(). This initialization only happens once, so if e.g. I try to do something that requires the HmacSHA512 algorithm it is not available and causes a NoSuchAlgorithmException.

This wasn't happening with Update 281.

REGRESSION : Last worked in version 8

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The test class below requires the BouncyCastle library, but I guess any class from a signed jar would work that uses an algorithm that comes from a provider. In my example the verification tries to find the "SHA256withRSA" algorithm during Verification. The Security.getProviders() call in this context uses a subset of the registered providers, the com.sun.crypto.provider.SunJCE one is not among them like it normally would be.

This is an example that directly accesses the AlgorithmId class, but in my real world example this would be triggered indirectly through saving a HmacSHA512 Key to a PKCS12 Keystore that is used in a JWT Authentication service.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The code should run without any exceptions on Update 291.
ACTUAL -
This runs fine on Version before Update 291. With Update 291 this causes the following exception:

Exception in thread "main" java.security.NoSuchAlgorithmException: unrecognized algorithm name: HmacSHA512
	at sun.security.x509.AlgorithmId.get(AlgorithmId.java:448)
	at Test.main(Test.java:13)

---------- BEGIN SOURCE ----------
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import sun.security.x509.AlgorithmId;

public class Test {
  public static void main(String[] args) throws Exception {
    new BouncyCastleProvider();

    AlgorithmId alg = AlgorithmId.get("HmacSHA512");
    System.out.println(alg);
  }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
None that I have found and would have the same result as with older Java Versions

FREQUENCY : always



Comments
[~sgehwolf] I updated it to be a duplicate of JDK-8242565, but it still seems have more in common with JDK-8266929. If the test case had used an algorithm that only BC supported, it would exhibit the same symptoms. Regardless, I agree JDK-8242565 should be backported.
18-05-2021

[~mullan] No, JDK-8266929 doesn't seem to be a duplicate of JDK-8242565. The difference seems to be AlgorithmId.get("HmacSHA512") vs. AlgorithmId.get("SHA384WITHDSA"). The former is provided by the SunJCE provider and the latter only by the bouncycastle provider. JDK-8242565 adds the SunJCE provider to the list of providers for jar verification. It's also why I don't seem to reproduce with JDK 11.0.11. 11.0.11 has JDK-8242565.
18-05-2021

The sun.security.x509.AlgorithmId keeps a "aliasOidsTable" static member which is a map of OID to algorithm name and the other way around - algorithm name to OID mappings. This "aliasOidsTable" gets initialized (only) once on lazy first access and gets stored for the lifetime of this sun.security.x509.AlgorithmId class. The initialization process of this "aliasOidsTable" mainly involves iterating over the list of "java.security.Provider" instances returned by "java.lang.security.Security.getProviders()" method. Each of these providers is iterated over and based on a specific logic in the, "collectOIDAliases" method, "aliasOidsTable" is populated. This "aliasOidsTable" which essentially is a cache, then gets used, either directly or indirectly, in 3 methods of that AlgorithmId class: public static AlgorithmId get(AlgorithmParameters algparams); public static AlgorithmId get(String algname); public String getName(); Given that the timing of the initialization of the cache (although lazy) is non-deterministic, there's no guarantee which Provider(s) are already registered and returned by Security.getProviders(). Furthermore, the list of providers itself is mutable at runtime, since the `java.lang.security.Security` class exposes methods like: public static synchronized void removeProvider(String name); public static int addProvider(Provider provider); public static synchronized int insertProviderAt(Provider provider, int position); which means that having a one-time built cache, based off the list of "java.security.Provider" returned by "java.lang.security.Security.getProviders()" will not work and will have to be reset/refreshed every time the list of Provider(s) gets updated. There's a similar update/refresh that is done for a cached default secure random through JCAUtil.clearDefSecureRandom() in a different part of the code. Perhaps something similar would be needed here. Or maybe the "aliasOidsTable" cache can be removed? In theory that cache does helps since you prevent iterating over the Provider(s) list frequently, but in practice unless there's a refresh/reset mechanism/callback, this cache can end up being stale.
18-05-2021

[~sgehwolf] Ok, do you mean then that JDK-8266929 is also a duplicate of JDK-8242565? If not, what is the difference between this issue and JDK-8266929?
18-05-2021

[~mullan] I'm pretty sure this is a duplicate of JDK-8242565, not JDK-8266929. At least I'm not able to reproduce with the JDK-8242565 backport.
18-05-2021

This seems a duplicate of JDK-8242565 (which adds the SunJCE provider to the list of providers for jar verification). Adding a link.
18-05-2021

Additional information from the submitter: I uploaded the entire project for you at https://gitlab.com/tommytt/report9163005 I���m running that project in IntelliJ and with Version 282 the test passes. If I switch to version 292 I get the following exception: java.security.NoSuchAlgorithmException: unrecognized algorithm name: HmacSHA512 at sun.security.x509.AlgorithmId.get(AlgorithmId.java:448) at Case.main(Case.java:9) at CaseTest.testExecuteWithoutException(CaseTest.java:8) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:628) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:117) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:184) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:180) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:127) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) at java.util.ArrayList.forEach(ArrayList.java:1259) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) at java.util.ArrayList.forEach(ArrayList.java:1259) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229) at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197) at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
29-04-2021

The observations on Windows 10: Oracle JDK 8u301ea+2: Passed. Open JDK 8u282: Passed. Open JDK 8u292: Failed, NoSuchAlgorithmException thrown.
29-04-2021

Requested the submitter to confirm the above observation.
29-04-2021

Got the following exception with JDK 8u301: Exception in thread "main" java.lang.NoSuchFieldError: id_alg_AEADChaCha20Poly1305
29-04-2021