从 OpenJDK16

问题描述

TL;DR

keytool 来自 OpenJDK16 创建无法从 Java 8、9、10 和 11 读取的 PKCS12 密钥库文件。这是错误吗?如何创建适用于 Java 8 的 PKCS12 密钥库?

上下文

我构建了一个 Maven 项目,该项目生成一个可执行 JAR 文件,该文件必须在从版本 8 到版本 16 的任何 JRE 上运行。该 JAR 文件生成一个 HTTPS 服务器(使用 com.sun.net.httpserver.HttpsServer em>).

在构建期间,我使用 keytool 生成密钥对并将其存储在捆绑在 JAR 中的 PKCS12 密钥库中(实际上,我使用的是 keytool-maven-plugin):

$ /path/to/jdk16/bin/keytool -genkeypair -keystore /tmp/keystore.p12 -storepass password -storetype PKCS12 -alias https -dname "CN=localhost,OU=My HTTP Server,O=Sentry Software,C=FR" -keypass password -validity 3650 -keyalg RSA -sigalg SHA256withRSA

Java 代码使用这个自动生成的密钥库来启动 HTTPS 服务器:

// initialize the HTTPS server
httpsServer = HttpsServer.create(socketAddress,0);

// initialize the keystore
KeyStore keyStore = KeyStore.getInstance("PKCS12");

// Load the self-certificate that is bundled with the JAR (see pom.xml)
InputStream ksstream = this.getClass().getResourceAsstream("/keystore.p12");
keyStore.load(ksstream,"password".tochararray()); // Exception here

// Rest of the code (only for context purpose)

// setup the key manager factory
String defaultKeyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(defaultKeyManagerAlgorithm);
keyManagerFactory.init(keyStore,"password".tochararray());

// setup the trust manager factory
String defaultTrustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(defaultTrustManagerAlgorithm);
trustManagerFactory.init(keyStore);

// setup the HTTPS context and parameters
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(),trustManagerFactory.getTrustManagers(),null);

// Sets the default SSL configuration (no need for extra code here!)
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));

问题

当 JAR 使用 OpenJDK 16 JDK 构建(并且使用来自 OpenJDK 16 的 keytool)然后在 Java 8 JRE 中执行时,我们在 keyStore.load() 上收到此异常:

IOException: parseAlgParameters Failed: ObjectIdentifier() -- data isn't an object ID (tag = 48)

在 OpenJDK 11.0.7+10 中执行同一个 JAR 时,我们得到这个异常:

IOException: Integrity check Failed: java.security.NoSuchAlgorithmException: Algorithm HmacPBESHA256 not available

但是,当使用 OpenJDK 14、15 或 16 执行相同的 JAR 时,没有例外,一切正常。

下表总结了 keytool 的版本,以及每个版本的 keytool 创建的 PKCS12 密钥库是否可以在各种 JRE 版本中加载:

JRE 8 JRE 11 JRE 14 JRE 16
keytool 8
keytool 11
keytool 14
keytool 15
keytool 16

问题

这是 keytoolKeyStore 类中的错误吗?

如何使用 OpenJDK16 创建一个在加载 JRE 8 时可以工作的 PKCS12 密钥库?

什么是HmacPBESHA256?我没有在我的 keytool 命令行中指定这个算法。

解决方法

这不是 keytoolKeyStore 中的错误。 OpenJDK 16 has been improved 中的 keytool 默认使用更安全的算法,Java 8 和 Java 11 不支持这些算法(请参阅 JDK-8228481)。

解决问题的2个选项:

  • 使用 JKS 而不是 PKCS12 来存储密钥
  • 使用 -J-Dkeystore.pkcs12.legacy 选项和 keytool 设置 keystore.pkcs12.legacy 系统属性并强制 OpenJDK 16 的 keytool 使用旧算法(Java 8 和 11 支持) )

对于 keytool-maven-plugin Maven 插件,使用以下配置:

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>keytool-maven-plugin</artifactId>
                <version>1.5</version>
                <executions>
                    <execution>
                        <id>create-https-certificate</id>
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>generateKeyPair</goal>
                        </goals>
                        <configuration>
                            <alias>https</alias>
                            <dname>CN=localhost,OU=${project.name},O=Sentry Software,C=FR</dname>
                            <keyalg>RSA</keyalg>
                            <keypass>password</keypass>
                            <keystore>${project.build.outputDirectory}/keystore.p12</keystore>
                            <sigalg>SHA256withRSA</sigalg>
                            <skipIfExist>true</skipIfExist>
                            <storepass>password</storepass>
                            <storetype>PKCS12</storetype>
                            <validity>3650</validity>
                            <workingDirectory>${project.build.directory}</workingDirectory>
                            <arguments>
                                <!-- Important! Use this to make sure the PKCS12 keystore can be used -->
                                <!-- with Java 8 and 11 -->
                                <!-- See https://bugs.openjdk.java.net/browse/JDK-8228481 -->
                                <!-- See https://stackoverflow.com/questions/67766268/ -->
                                <argument>-J-Dkeystore.pkcs12.legacy</argument>
                            </arguments>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

注意:所有学分归@dave_thompson_085