"java.io.IOException: Invalid secret key format" when opening JCEKS key store with Oracle Java 8 JRE 172

wilx picture wilx · May 17, 2018 · Viewed 7.3k times · Source

I am getting the following exception when I try to open JCEKS type key store with with Oracle Java 8 JRE 172 on Windows. This worked fine with earlier versions of the JRE:

INFO: ObjectInputFilter REJECTED: null, array length: -1, nRefs: 1, depth: 1, bytes: 70, ex: n/a
[...call stacks omitted to protect the innocent...]
Caused by: java.io.IOException: Invalid secret key format
        at com.sun.crypto.provider.JceKeyStore.engineLoad(JceKeyStore.java:856)
        at java.security.KeyStore.load(Unknown Source)
[...]

This looks very much like JDK-8202506 but I use Java 8 and I get null in the initial INFO message.

Is this the same issue?

It seems to me the JDK-8202506 issue is currently not fixed in any public JRE release. Am I right?

UPDATE 1

This looks similar and they also have no solution: ATLAS-2642

UPDATE 2

For some reason, Equinox fails to see the com.sun.crypto.provider.SealedObjectForKeyProtector class after the upgrade, even though it is clearly in the JRE that comes with the new JDK:

BundleClassLoader[foo.bar.baz.crypto].loadClass(com.sun.crypto.provider.SealedObjectForKeyProtector) failed.
java.lang.ClassNotFoundException: com.sun.crypto.provider.SealedObjectForKeyProtector
    at org.eclipse.osgi.framework.internal.core.BundleLoader.findClassInternal(BundleLoader.java:481)
    at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:397)
    at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:385)
    at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:87)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:686)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1866)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1749)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2040)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1571)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
    at com.sun.crypto.provider.JceKeyStore.engineLoad(JceKeyStore.java:850)
    at java.security.KeyStore.load(KeyStore.java:1445)

UPDATE 3

The class SealedObjectForKeyProtector.class is somehow different from the rest of the classes in the sunjce_provider.jar. When we try to decompile it with JD-GUI, it fails with internal error, unlike the rest of the classes:

JD-GUI failure to decompile SealedObjectForKeyProtector.class

Answer

Geoffrey Chen picture Geoffrey Chen · Oct 1, 2018

I meet this issue these days. And per my troubleshooting, it is caused by the different return value of this method:

sun.misc.VM.latestUserDefinedLoader()

Previously (before 8u171), this method returns sun.misc.Launcher$ExtClassLoader, while it returns application's classloader after upgrade. In ObjectInputStream, both class loader can load com.sun.crypto.provider.SealedObjectForKeyProtector successfully, that's simply because ExtClassLoader is the parent of application's class loader (or, parent's parent). However, once SealedObjectForKeyProtector is loaded by application's class loader, it's class loader no longer equals to ExtClassLoader.

On the other hand, within com.sun.crypto.provider.JceKeyStore, unlike ObjectInputStream, SealedObjectForKeyProtector is always loaded by ExtClassLoader. Thus below check in JceKeyStore.java:932 will fail due to class doesn't equal:

932            if (info.serialClass() != SealedObjectForKeyProtector.class))
934                return Status.REJECTED;

Then, we will get below log and an IOException eventually: ObjectInputFilter REJECTED: class com.sun.crypto.provider.SealedObjectForKeyProtector

Solution: make sure class com.sun.crypto.provider.SealedObjectForKeyProtector is not loaded by ContextClassLoader by certain configuration. Details depend on the ContextClassLoader. For example, for org.powermock.core.classloader.MockClassLoader, the concrete solution is adding below annotation to involved test classes:

@PowerMockIgnore("com.sun.*")