【发布时间】:2021-08-18 07:39:35
【问题描述】:
TL;DR
来自 OpenJDK16 的keytool 创建无法从 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));
问题
当使用 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 时,没有例外,一切正常。
下表总结了keytool的版本,以及使用keytool的每个版本创建的PKCS12密钥库是否可以在各种JRE版本中加载:
| JRE 8 | JRE 11 | JRE 14 | JRE 16 | |
|---|---|---|---|---|
| keytool 8 | ✅ | ✅ | ✅ | ✅ |
| keytool 11 | ✅ | ✅ | ✅ | ✅ |
| keytool 14 | ✅ | ✅ | ✅ | ✅ |
| keytool 15 | ✅ | ✅ | ✅ | ✅ |
| keytool 16 | ⛔ | ⛔ | ✅ | ✅ |
问题
这是 keytool 或 KeyStore 类中的错误吗?
如何使用 OpenJDK16 创建一个在加载 JRE 8 时可以工作的 PKCS12 密钥库?
什么是HmacPBESHA256?我没有在我的keytool 命令行中指定这个算法。
【问题讨论】:
-
这是 keytool 中的一个更改,从 11.0.0(包括所有 8 个)和一个 限制bug(格式错误的 PBKDF2)暴露出来/i> 在 11.0.1-11 中。请参阅 the release notes 并 (1) 按照文件中的说明设置安全属性或 (2) 使用已弃用但仍支持兼容性的
-storetype jks或jceks创建,并相应地更改您的代码。 -
当然也可以使用较旧的keytool,或者如果出于某种原因您不想要多个java,您可以也可以使用
openssl req -new -x509 ...; openssl pkcs12 -export ...(如无数其他问题中所述)在旧式 PKCS12 中创建密钥对和自签名证书。 PS:服务器在其 TrustManager 中不需要自己的证书,除非您也将它用作 DIY CA 来向客户端颁发证书。 -
这将如何运作?谁会相信你的证书?怎么样?
-
@dave_thompson_085 谢谢!这是corresponding official issue。由于这是一个自我证书,我不需要额外的安全性,所以我将使用
-J-Dkeystore.pkcs12.legacy选项和keytool。 -
@dave_thompson_085 请将您的答案添加为“真实”答案,以便我将其标记为正确,谢谢! :-)(或者我自己添加答案)