6

当我尝试在 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,并且我收到null了初始的 INFO 消息。

这是同一个问题吗?

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

更新 1

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

更新 2

出于某种原因,Equinoxcom.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 反编译 SealedObjectForKeyProtector.class 失败

4

3 回答 3

4

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

sun.misc.VM.latestUserDefinedLoader()

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

另一方面com.sun.crypto.provider.JceKeyStore,与 不同ObjectInputStreamSealedObjectForKeyProtector是,within 始终由 ExtClassLoader 加载。因此,以下检查JceKeyStore.java:932将由于类不等于而失败:

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

然后,我们最终会得到下面的日志和一个 IOException: ObjectInputFilterREJECTED: classcom.sun.crypto.provider.SealedObjectForKeyProtector

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

@PowerMockIgnore("com.sun.*")

于 2018-10-01T14:29:22.570 回答
2

我已经对该问题进行了完整的分析,并通过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 的上述代码中,过滤器信息将始终为空,因此 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 插件项目的 META-INF 中使用 Eclipse-BuddyPolicy:ext。
于 2019-03-26T11:23:38.917 回答
1

我目前正在与 Oracle JRE Support 合作,并打开了一个私人错误。到目前为止我得到的信息:

  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 环境来验证它。感谢是否有人可以尝试并让我们知道结果。

于 2018-07-29T03:18:35.300 回答