使用Oracle Java 8 JRE打开JCEKS密钥库时出现“java.io.IOException:无效的密钥格式”172

时间:2018-05-17 14:06:06

标签: java osgi equinox jce jceks

当我尝试在Windows上使用Oracle Java 8 JRE 172打开JCEKS类型密钥存储区时,我收到以下异常。这适用于早期版本的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)
[...]

这看起来非常像JDK-8202506,但我使用Java 8,并在初始INFO消息中得到null

这是同一个问题吗?

在我看来,JDK-8202506问题目前在任何公共JRE版本中都没有修复。我是对的吗?

更新1

这看起来很相似,也没有解决方案:ATLAS-2642

更新2

出于某种原因,Equinox在升级后未能看到com.sun.crypto.provider.SealedObjectForKeyProtector类,即使它显然位于新JDK附带的JRE中:

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)

更新3

SealedObjectForKeyProtector.class在某种程度上与sunjce_provider.jar中的其他类不同。当我们尝试使用JD-GUI对其进行反编译时,它会因内部错误而失败,与其他类不同:

JD-GUI failure to decompile SealedObjectForKeyProtector.class

3 个答案:

答案 0 :(得分:4)

这些天我遇到了这个问题。根据我的故障排除,这是由该方法的不同返回值引起的:

sun.misc.VM.latestUserDefinedLoader()

以前(在8u171之前),此方法返回sun.misc.Launcher$ExtClassLoader,而升级后返回应用程序的类加载器。在ObjectInputStream中,两个类加载器都可以成功加载com.sun.crypto.provider.SealedObjectForKeyProtector,这仅仅是因为ExtClassLoader是应用程序的类加载器的父级(或父级的父级)。但是,一旦应用程序的类加载器加载了SealedObjectForKeyProtector,它的类加载器将不再等于ExtClassLoader。

另一方面,在com.sun.crypto.provider.JceKeyStore中,与ObjectInputStream不同,SealedObjectForKeyProtector始终由ExtClassLoader加载。因此下面的JceKeyStore.java:932签入将由于类不相等而失败:

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

然后,我们将获得下面的日志和最终的IOException: ObjectInputFilter已拒绝:类com.sun.crypto.provider.SealedObjectForKeyProtector

解决方案:确保通过某些配置ContextClassLoader未加载类com.sun.crypto.provider.SealedObjectForKeyProtector。详细信息取决于ContextClassLoader。例如,对于org.powermock.core.classloader.MockClassLoader,具体解决方案是在涉及的测试类中添加以下注释:

@PowerMockIgnore("com.sun.*")

答案 1 :(得分:1)

我目前正在为此使用Oracle JRE支持,并且已打开一个私人错误。到目前为止我得到的信息:

  1. 它与JDK-8202506不同。
  2. 是的,这是Equinox在SealedObjectForKeyProtector上的类加载问题。

解决方案:

一种解决方法是将以下行添加到OSGi软件包MANIFEST.MF中。

Eclipse-BuddyPolicy:扩展

我亲自通过JRE 1.8_181验证了此变通办法,它似乎可以正常工作。

我还被告知,对于Java 9,JVM参数-Dosgi.compatibility.bootdelegation = true可以完成这项工作(无需更新MANIFEST.MF),但是我没有Java 9环境验证一下。感谢有人可以尝试一下并让我们知道结果。

答案 2 :(得分:1)

我已经对该问题进行了完整的分析,并通过涉及JCEKS密钥库的代码部分进行了调试。每当应用程序使用自定义类加载器时,如果使用来自JDK 8 Update 151之前的Java版本的JCEKS密钥库,您肯定会遇到问题。

 private static class DeserializationChecker implements ObjectInputFilter {
    private static final int MAX_NESTED_DEPTH = 2;

    @Override
    public ObjectInputFilter.Status
        checkInput(ObjectInputFilter.FilterInfo info) {

        // First run a custom filter
        long nestedDepth = info.depth();
        if ((nestedDepth == 1 &&
                    info.serialClass() != SealedObjectForKeyProtector.class) ||
                (nestedDepth > MAX_NESTED_DEPTH &&
                    info.serialClass() != null &&
                    info.serialClass() != Object.class)) {
            return Status.REJECTED;
        }

        // Next run the default filter, if available
        ObjectInputFilter defaultFilter =
            ObjectInputFilter.Config.getSerialFilter();
        if (defaultFilter != null) {
            return defaultFilter.checkInput(info);
        }

        return Status.UNDECIDED;
    }
}

在上面的JceKeyStore.class代码中,过滤器信息将始终为null,因此 info.serialClass()!= SealedObjectForKeyProtector.class 检查将失败。在像Equinox-Eclipse插件这样的自定义类加载器的情况下,类加载委派不会按预期发生。

有两步解决方案

  1. 首先将密钥库从JCEKS升级到PKCS12,如JDK 151 https://www.oracle.com/technetwork/java/javase/8u151-relnotes-3850493.html中“注意:更好的密钥库处理”主题下所述。对于大多数情况,这将解决问题。更改后,进一步访问密钥库类型值为PKCS12的密钥库。
  2. 使sun_jce提供程序中的类早于您的自定义类加载器作用域加载。使用这些配置,使您的自定义类加载器将sun_jce jar的类加载委托给ExtClassLoader。例如:通过使用Eclipse-BuddyPolicy:eclipse插件项目的META-INF中的ext。