TL;DR
keytool
from OpenJDK16 creates PKCS12 keystore files that cannot be read from Java 8, 9, 10 and 11. Is this a bug? How to create a PKCS12 keystore that works with Java 8?
Context
I build a Maven project which produces an executable JAR file that must run on any JRE from version 8 to version 16. That JAR file spawns an HTTPS server (using com.sun.net.httpserver.HttpsServer).
During the build, I use keytool
to generate a key pair and store it in a PKCS12 keystore that is bundled in the JAR (actually, I'm using 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
The Java code uses this automatically-generated keystore to start the HTTPS server:
// 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));
Problem
When the JAR is build with a OpenJDK 16 JDK (and keytool
from OpenJDK 16 is used) and then executed in a Java 8 JRE, we get this exception on keyStore.load()
:
IOException: parseAlgParameters failed: ObjectIdentifier() -- data isn't an object ID (tag = 48)
When the same JAR is executed in OpenJDK 11.0.7+10, we get this exception:
IOException: Integrity check failed: java.security.NoSuchAlgorithmException: Algorithm HmacPBESHA256 not available
However, when the same JAR is executed with OpenJDK 14, 15 or 16, no exception and everything works.
Here's a table that summarizes the versions of keytool
, and whether the PKCS12 key store created with each version of keytool
can be loaded in various JRE versions:
JRE 8 | JRE 11 | JRE 14 | JRE 16 | |
---|---|---|---|---|
keytool 8 | ✅ | ✅ | ✅ | ✅ |
keytool 11 | ✅ | ✅ | ✅ | ✅ |
keytool 14 | ✅ | ✅ | ✅ | ✅ |
keytool 15 | ✅ | ✅ | ✅ | ✅ |
keytool 16 | ⛔ | ⛔ | ✅ | ✅ |
Questions
Is this a bug in keytool
, or in the KeyStore class?
How to create a PKCS12 key store using OpenJDK16 that will work when loaded with JRE 8?
What is HmacPBESHA256? I haven't specified this algorithm in my keytool
command line.