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?
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));
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 | ⛔ | ⛔ | ✅ | ✅ |
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.
It's not a bug in keytool
or KeyStore. keytool
in 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 options to solve the problem:
-J-Dkeystore.pkcs12.legacy
option with keytool
to set the keystore.pkcs12.legacy
system property and force OpenJDK 16's keytool
to use the older algorithms (which are supported by Java 8 and 11)For the keytool-maven-plugin Maven plugin, use the below configuration:
<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>
Note: All credits go to @dave_thompson_085
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With