Java 8 读取使用 OpenJDK16 中的 keytool 创建的 PKCS12 密钥库时出现 IOException
IOException in Java 8 when reading PKCS12 keystore created with keytool from OpenJDK16
TL;DR
keytool
from OpenJDK16 创建无法从 Java 8、9、10 和 11 读取的 PKCS12 密钥库文件。这是一个错误吗?如何创建适用于 Java 8?
的 PKCS12 密钥库
上下文
我构建了一个 Maven 项目,该项目生成一个 executable JAR 文件,该文件必须在版本 8 到版本 16 的任何 JRE 上 运行。该 JAR 文件生成一个 HTTPS 服务器(使用 com.sun.net.httpserver.HttpsServer).
在构建过程中,我使用 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));
问题
当使用 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 时,没有异常,一切正常。
这里有一个table总结了keytool
的版本,用keytool
每个版本创建的PKCS12密钥库是否可以在各种JRE版本中加载:
JRE 8
JRE 11
JRE 14
JRE 16
keytool 8
✅
✅
✅
✅
keytool 11
✅
✅
✅
✅
keytool 14
✅
✅
✅
✅
keytool 15
✅
✅
✅
✅
keytool 16
⛔
⛔
✅
✅
问题
这是 keytool
中的错误,还是 KeyStore class 中的错误?
如何使用 OpenJDK16 创建 PKCS12 密钥库,它在加载 JRE 8 时可以工作?
什么是 HmacPBESHA256?我没有在我的 keytool
命令行中指定这个算法。
这不是 keytool
或 KeyStore 中的错误。 keytool
在 OpenJDK 16 has been improved to use more secure algorithms by default, which are not supported with Java 8 and Java 11 (see 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 -->
<argument>-J-Dkeystore.pkcs12.legacy</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
注意:所有学分归于@dave_thompson_085
TL;DR
keytool
from OpenJDK16 创建无法从 Java 8、9、10 和 11 读取的 PKCS12 密钥库文件。这是一个错误吗?如何创建适用于 Java 8?
上下文
我构建了一个 Maven 项目,该项目生成一个 executable JAR 文件,该文件必须在版本 8 到版本 16 的任何 JRE 上 运行。该 JAR 文件生成一个 HTTPS 服务器(使用 com.sun.net.httpserver.HttpsServer).
在构建过程中,我使用 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));
问题
当使用 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 时,没有异常,一切正常。
这里有一个table总结了keytool
的版本,用keytool
每个版本创建的PKCS12密钥库是否可以在各种JRE版本中加载:
JRE 8 | JRE 11 | JRE 14 | JRE 16 | |
---|---|---|---|---|
keytool 8 | ✅ | ✅ | ✅ | ✅ |
keytool 11 | ✅ | ✅ | ✅ | ✅ |
keytool 14 | ✅ | ✅ | ✅ | ✅ |
keytool 15 | ✅ | ✅ | ✅ | ✅ |
keytool 16 | ⛔ | ⛔ | ✅ | ✅ |
问题
这是 keytool
中的错误,还是 KeyStore class 中的错误?
如何使用 OpenJDK16 创建 PKCS12 密钥库,它在加载 JRE 8 时可以工作?
什么是 HmacPBESHA256?我没有在我的 keytool
命令行中指定这个算法。
这不是 keytool
或 KeyStore 中的错误。 keytool
在 OpenJDK 16 has been improved to use more secure algorithms by default, which are not supported with Java 8 and Java 11 (see 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 -->
<argument>-J-Dkeystore.pkcs12.legacy</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
注意:所有学分归于@dave_thompson_085