JDK-8270170 : Cannot use '-Djava.system.class.loader' with class loader in signed JAR
  • Type: Bug
  • Component: security-libs
  • Sub-Component: java.security
  • Affected Version: 11.0.11
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2021-07-08
  • Updated: 2022-01-28
  • Resolved: 2022-01-28
Related Reports
Cloners :  
Duplicate :  
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
The problem only occurs on JDK 11.0.11, not on 11.0.10. Also no problem on the other ones I tested: 9.0.4, 13.0.2, 14.0.2, 15.0.2, 16. I tested on Windows 10 Professional.

A DESCRIPTION OF THE PROBLEM :
A detailed description can be found in my StackOverflow answer here:
https://stackoverflow.com/a/68295398/1082681

The problem occurs if and only if
a) -Djava.system.class.loader is used and
b) the system class loader in question is found in a signed JAR and
c) the application runs on JDK 11.0.11 (not 11.0.10 or any other JDK 9-16 I tested).

As soon as I remove the JAR signature from the META-INF directory, it also works on JDK 11.0.11. IMO, this is a regression bug in the JDK and should be fixed. 

REGRESSION : Last worked in version 11

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Source code for minimal test case see below.

Please make sure that both classes are in different packages, otherwise later the JVM will complain about not both of them being signed.

We are is a base directory, the source code is in folder 'src'.

------------------------------------------------------------------------

# Compile source files (JDK used for compilation is unimportant)
javac --release 8 src\org\acme\app\Main.java src\org\acme\loader\CustomClassLoader.java

# Create JAR containing custom class loader
jar cf CustomClassLoader.jar -C src org\acme\loader\CustomClassLoader.class

# Create signing key (default keystore has password 'changeit')
keytool -genkeypair -keyalg RSA -alias test-user

# Sign JAR (default keystore has password 'changeit')
jarsigner CustomClassLoader.jar test-user

# Run dummy application, setting custom class loader from JAR as system class loader
"c:\Program Files\Java\jdk-11.0.11\bin\java.exe" -Djava.security.debug="jca" -Djava.system.class.loader=org.acme.loader.CustomClassLoader -cp "CustomClassLoader.jar;src" org.acme.app.Main

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Output should be:

ProviderList: provider configuration: [SUN, SunRsaSign, SunEC, SunJSSE, SunJCE, SunJGSS, SunSASL, XMLDSig, SunPCSC, JdkLDAP, JdkSASL, SunMSCAPI, SunPKCS11]
ProviderList: config configuration: null
ProviderList: ThreadLocal providers: [SUN, SunRsaSign, SunEC]
ProviderList: Disabling ThreadLocal providers
ProviderList: ThreadLocal providers: [SUN, SunRsaSign, SunEC]
ProviderList: Disabling ThreadLocal providers
ACTUAL -
Actual output is:

ProviderList: provider configuration: [SUN, SunRsaSign, SunEC, SunJSSE, SunJCE, SunJGSS, SunSASL, XMLDSig, SunPCSC, JdkLDAP, JdkSASL, SunMSCAPI, SunPKCS11]
ProviderList: config configuration: null
ProviderList: ThreadLocal providers: [SUN, SunRsaSign, SunEC, SunJCE]
ProviderList: Disabling ThreadLocal providers
ProviderList: ThreadLocal providers: [SUN, SunRsaSign, SunEC, SunJCE]
ProviderList: Loading all providers
java.lang.Exception: Debug Info. Call trace:
        at java.base/sun.security.jca.ProviderList.loadAll(ProviderList.java:311)
        at java.base/sun.security.jca.ProviderList.removeInvalid(ProviderList.java:332)
        at java.base/sun.security.jca.Providers.getFullProviderList(Providers.java:165)
        at java.base/java.security.Security.getProviders(Security.java:457)
        at java.base/sun.security.x509.AlgorithmId.computeOidTable(AlgorithmId.java:632)
        at java.base/sun.security.x509.AlgorithmId.oidTable(AlgorithmId.java:622)
        at java.base/sun.security.x509.AlgorithmId.algOID(AlgorithmId.java:604)
        at java.base/sun.security.x509.AlgorithmId.get(AlgorithmId.java:436)
        at java.base/sun.security.pkcs.SignerInfo.verify(SignerInfo.java:379)
        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:872)
        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:521)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:398)
        at java.base/java.lang.ClassLoader.initSystemClassLoader(ClassLoader.java:1975)
        at java.base/java.lang.System.initPhase3(System.java:2070)
ProviderConfig: Loading provider SunEC
ProviderConfig: Error loading provider SunEC
java.lang.ExceptionInInitializerError
        at java.base/sun.security.jca.ProviderConfig$3.run(ProviderConfig.java:244)
        at java.base/sun.security.jca.ProviderConfig$3.run(ProviderConfig.java:238)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.base/sun.security.jca.ProviderConfig.doLoadProvider(ProviderConfig.java:238)
        at java.base/sun.security.jca.ProviderConfig.getProvider(ProviderConfig.java:218)
        at java.base/sun.security.jca.ProviderList.loadAll(ProviderList.java:315)
        at java.base/sun.security.jca.ProviderList.removeInvalid(ProviderList.java:332)
        at java.base/sun.security.jca.Providers.getFullProviderList(Providers.java:165)
        at java.base/java.security.Security.getProviders(Security.java:457)
        at java.base/sun.security.x509.AlgorithmId.computeOidTable(AlgorithmId.java:632)
        at java.base/sun.security.x509.AlgorithmId.oidTable(AlgorithmId.java:622)
        at java.base/sun.security.x509.AlgorithmId.algOID(AlgorithmId.java:604)
        at java.base/sun.security.x509.AlgorithmId.get(AlgorithmId.java:436)
        at java.base/sun.security.pkcs.SignerInfo.verify(SignerInfo.java:379)
        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:872)
        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:521)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:398)
        at java.base/java.lang.ClassLoader.initSystemClassLoader(ClassLoader.java:1975)
        at java.base/java.lang.System.initPhase3(System.java:2070)
Caused by: java.lang.IllegalStateException: getSystemClassLoader cannot be called during the system class loader instantiation
        at java.base/java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1931)
        at java.base/sun.security.jca.ProviderConfig$ProviderLoader.<init>(ProviderConfig.java:319)
        at java.base/sun.security.jca.ProviderConfig$ProviderLoader.<clinit>(ProviderConfig.java:309)
        ... 34 more
ProviderList: Disabling ThreadLocal providers

---------- BEGIN SOURCE ----------
package org.acme.app;

public class Main {
  public static void main(String[] args) {}
}

------------------------------------------------------------------------

package org.acme.loader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

public class CustomClassLoader extends ClassLoader {

  public CustomClassLoader(ClassLoader parent) {
    super(parent);
  }

  @Override
  public Class<?> findClass(String name) throws ClassNotFoundException {
    byte[] b = loadClassFromFile(name);
    return defineClass(name, b, 0, b.length);
  }

  private byte[] loadClassFromFile(String fileName) {
    InputStream inputStream = getClass().getClassLoader()
      .getResourceAsStream(fileName.replace('.', File.separatorChar) + ".class");
    byte[] buffer;
    ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
    int nextValue = 0;
    try {
      while ((nextValue = inputStream.read()) != -1) {
        byteStream.write(nextValue);
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
    buffer = byteStream.toByteArray();
    return buffer;
  }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Do not sign the JAR or remove an existing JAR signature from META-INF.

FREQUENCY : always



Comments
Marking this as a duplicate of JDK-8266929. Will open a new bug which affects JDK 18, which is a different issue.
28-01-2022

This issue was fixed in 11.0.13. See https://bugs.openjdk.java.net/browse/JDK-8266929. JDK 18 fails for a different reason though, so still looking into that.
27-01-2022

Looks like it is a regression caused by https://bugs.openjdk.java.net/browse/JDK-8249906. Will report back after further analysis.
27-01-2022

If there is a custom system class loader set then it is loaded in initPhase3. VM.isBooted return false during this phase of startup and maybe there is security code or service loading needed to check the signed JAR and this fails.
20-01-2022

Additional Information from the submitter: ============================== The person verifying the bug report said he/she has a problem with an exception, as described in the comment. I can see that the command line has a little error: Actual classpath: -cp "CustomClassLoader.jar" Classpath I said you should use (see my bug report): -cp "CustomClassLoader.jar;src" If you add the missing directory, I guess you should be fine. Anyway, the error is unrelated to the problem as such. The important error is the one seen on 11.0.11.
12-07-2021

The observations on Windows 10: JDK 11.0.10+8: Passed. JDK 11.0.11+1: Failed, NoClassDefFoundError thrown. JDK 18ea+1: Failed, java.util.ServiceConfigurationError: Locale provider adapter "CLDR"cannot be instantiated.
09-07-2021

The issue is reproducible with 11.0.11 for the given reproducer. However I could not get through with other builds other than 11.0.11 as mentioned. I got following error: > java '-Djava.security.debug="jca"' '-Djava.system.class.loader=org.acme.loader.CustomClassLoader' -cp "CustomClassLoader.jar" org.acme.app.Main ProviderList: provider configuration: [SUN, SunRsaSign, SunEC, SunJSSE, SunJCE, SunJGSS, SunSASL, XMLDSig, SunPCSC, JdkLDAP, JdkSASL, SunMSCAPI, SunPKCS11] ProviderList: config configuration: null ProviderList: ThreadLocal providers: [SUN, SunRsaSign, SunEC] ProviderList: Disabling ThreadLocal providers ProviderList: ThreadLocal providers: [SUN, SunRsaSign, SunEC] ProviderList: Disabling ThreadLocal providers Error occurred during initialization of VM java.lang.NoClassDefFoundError: app/CustomClassLoader (wrong name: org/acme/loader/CustomClassLoader) at java.lang.ClassLoader.defineClass1(java.base@9/Native Method) at java.lang.ClassLoader.defineClass(java.base@9/ClassLoader.java:1007) at java.security.SecureClassLoader.defineClass(java.base@9/SecureClassLoader.java:174) at jdk.internal.loader.BuiltinClassLoader.defineClass(java.base@9/BuiltinClassLoader.java:801) at jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(java.base@9/BuiltinClassLoader.java:699) at jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(java.base@9/BuiltinClassLoader.java:622) at jdk.internal.loader.BuiltinClassLoader.loadClass(java.base@9/BuiltinClassLoader.java:580) at jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(java.base@9/ClassLoaders.java:185) at java.lang.ClassLoader.loadClass(java.base@9/ClassLoader.java:496) at java.lang.Class.forName0(java.base@9/Native Method) at java.lang.Class.forName(java.base@9/Class.java:375) at java.lang.ClassLoader.initSystemClassLoader(java.base@9/ClassLoader.java:1967) at java.lang.System.initPhase3(java.base@9/System.java:2060)
09-07-2021