java - 从 OpenJDK16 读取使用 keytool 创建的 PKCS12 keystore 时,Java 8 中的 IOException

标签 java ssl keytool java-16

TL;博士keytool从 OpenJDK16 创建的 PKCS12 keystore 文件无法从 Java 8、9、10 和 11 读取。这是一个错误吗?如何创建适用于 Java 8 的 PKCS12 keystore ?
语境
我构建了一个 Maven 项目,该项目生成一个可执行的 JAR 文件,该文件必须在从版本 8 到版本 16 的任何 JRE 上运行。该 JAR 文件生成一个 HTTPS 服务器(使用 com.sun.net.httpserver.HttpsServer)。
在构建期间,我使用 keytool生成 key 对并将其存储在捆绑在 JAR 中的 PKCS12 keystore 中(实际上,我使用的是 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 代码使用这个自动生成的 keystore 来启动 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));

问题
当使用 OpenJDK 16 JDK 构建 JAR(并且使用来自 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 时,没有异常(exception),一切正常。
下表总结了 keytool 的版本。 ,以及是否使用 keytool 的每个版本创建的 PKCS12 keystore 可以在各种 JRE 版本中加载:



JRE 8
JRE 11
JRE 14
JRE 16


key 工具 8





key 工具 11





key 工具 14





key 工具 15





key 工具 16





问题
这是 keytool 中的错误吗? ,还是在 KeyStore 类中?
如何使用 OpenJDK16 创建一个 PKCS12 keystore ,该 keystore 在加载 JRE 8 时可以工作?
什么是 HmacPBESHA256?我没有在我的 keytool 中指定这个算法命令行。

最佳答案

这不是 keytool 中的错误或 keystore 。 keytoolOpenJDK 16 has been improved默认使用更安全的算法,Java 8 和 Java 11 不支持这些算法(参见 JDK-8228481 )。
解决问题的2个选项:

  • 使用 JKS 而不是 PKCS12 作为 keystore
  • 使用 -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

    关于java - 从 OpenJDK16 读取使用 keytool 创建的 PKCS12 keystore 时,Java 8 中的 IOException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67766268/

    相关文章:

    java - 使用 selenium webdriver 测试文件夹层次结构

    c# - 在c#中发送邮件到gmail ssl问题

    Apache Mina FTP 服务器 SFTP 配置和连接 SFTP 连接使用 FileZiza 或 WInscp 或任何 ftp 客户端接管安全通道

    java - 使用 JavaFX 将数据写入 MySQL 表

    java - 如何在方向改变时停止整个 Activity 娱乐

    java - Web 项目未在 Websphere 9 中启动,并出现 BValInterceptor 的 CDI 相关异常

    .htaccess - 如何在整个域上强制重定向 http https

    ssl - 如何在 Go 中建立到 memsql 的基于 ssl 的 tcp 连接

    java - 使用keytool创建服务器证书所需的最少步骤数

    java - Android keystore 签名问题