【问题标题】:"java.io.IOException: Invalid secret key format" when opening JCEKS key store with Oracle Java 8 JRE 172使用 Oracle Java 8 JRE 172 打开 JCEKS 密钥库时出现“java.io.IOException:无效的密钥格式”
【发布时间】:2018-05-17 14:06:06
【问题描述】:

当我尝试在 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 对其进行反编译时,它会因内部错误而失败,这与其他类不同:

【问题讨论】:

    标签: java osgi equinox jce jceks


    【解决方案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 的代码中,过滤器信息将始终为空,因此 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。

    【讨论】:

    • 我们最终以 PKCS12 格式重新实现了我们的密钥库。我想,已经有一段时间了,我试过Eclipse-BuddyPolicy: ext,它似乎没有帮助,但我不能完全确定。
    【解决方案2】:

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

    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

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

    @PowerMockIgnore("com.sun.*")

    【讨论】:

      【解决方案3】:

      我目前正在与 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 环境验证它。是否有人可以尝试并告诉我们结果。

      【讨论】:

      • 好吧,我急切地等待解决方案。 :)
      • 我用经过验证的解决方案添加了我的答案。
      • 我不确定这是否完全正确。它似乎对我没有影响。
      • @wilx 很抱歉知道。 :-( 我想知道你是否想通过谷歌驱动器或 Dropbox 等渠道与我分享你的测试用例。
      猜你喜欢
      • 2023-03-20
      • 2014-11-22
      • 2010-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-14
      • 2016-01-24
      • 2014-11-04
      相关资源
      最近更新 更多