I'm writing a Java 8 application and want to set up a simple keystore and truststore using a self-signed certificate.
Normally this goes as follows:
- Create a keypair + certificate using
openssl. - Create a .jks keystore + .jks truststore using
keytool
Now I'd like to only use openssl and create .p12 keystores instead of .jks keystores.
Creating a .p12 keystore works great using the following commands:
# Create private key and certificate
openssl req -x509 -newkey rsa:"${rsa}" -sha256 \
-keyout "${key}" \
-out "${cert}" \
-days "${days}"
# Create .p12 keystore
openssl pkcs12 -export -in "${cert}" -inkey "${key}" -out "${keystore}"
This keystore seems to be working correctly, as providing a corresponding .jks trustore in my Java application will get the TLS connection going. However I can't get a .p12 truststore working.
I tried creating the truststore as suggested here:
# Create .p12 truststore
openssl pkcs12 -export -nokeys -in "${cert}" -out "${truststore}"
and then loading it like this:
FileInputStream fis = new FileInputStream(new File(trustorePath));
KeyStore trustStore = KeyStore.getInstance("PKCS12");
trustStore.load(fis, truststorePassword.toCharArray());
fis.close();
but I receive the following exception in my java code:
Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
Any ideas what I'm doing wrong?
(A working snippet using .p12 truststore with Java 8 would be greatly appreciated.)
5 Answers 5
A possible explanation for this behaviour:
The standard PKCS#12 provider up to Java 7 did not allow trusted certificate entries at all. The JSSE Reference Guide says this:
Storing trusted certificates in a PKCS12 keystore is not supported. PKCS12 is mainly used to deliver private keys with the associated certificate chains. It does not have any notion of "trusted" certificates. In terms of interoperability, other PKCS12 vendors have the same restriction. Browsers such as Mozilla and Internet Explorer do not accept a PKCS12 file with only trusted certificates.
This has changed a bit in Java 8, which supports trusted certificates in PKCS#12 - if they are marked with a special attribute (OID 2.16.840.1.113894.746875.1.1):
openssl pkcs12 -in microsoft.p12 -info MAC Iteration 1024 MAC verified OK PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 1024 Certificate bag Bag Attributes friendlyName: microsoft it ssl sha2 (baltimore cybertrust root) 2.16.840.1.113894.746875.1.1: <Unsupported tag 6>
Source:
4 Comments
This has beed fixed in OpenSSL 3.2+ (commit). Now you can create a p12 truststore directly with OpenSSL.
A jdktrust option has been added. It has one option, anyExtendedKeyUsage.
# Create a p12 truststore that can be directly used with java without a password.
args=()
args+=(-in "$pem_file")
args+=(-out "$truststore_file")
# Disable password. This is only certs--no key. No need for encryption.
args+=(-keypbe NONE -certpbe NONE -nomaciter -passout pass:)
# See
# https://docs.openssl.org/3.2/man1/openssl-pkcs12/#pkcs12-output-export-options
# for 'jdktrust' option.
# https://github.com/openssl/openssl/commit/21f7a09ca256eee0ccc9a8fc498e8427469ab506
# added the CLI option 'jdktrust' in openssl 3.2+ which does:
# Export pkcs12 file in a format compatible with Java keystore
# usage. This option accepts a string parameter indicating the
# trust oid name to be granted to the certificate it is
# associated with. Currently only "anyExtendedKeyUsage"
# is defined. `-jdktrust` adds `-nokeys`.
args+=(-jdktrust anyExtendedKeyUsage)
openssl pkcs12 -export "${args[@]}"
# Verify the truststore works with java.
keytool -list -keystore "$truststore_file" -storepass ""
Comments
I haven't found the way to do it with openssl, though I've found the way to do it with keytool (a part of OpenJDK suite).
I needed to create a keystore which would contain the CA certificate with the 2.16.840.1.113894.746875.1.1: <Unsupported tag 6> attribute and an additional certificate and its key without this attribute. I'd like to share the solution with other people who will find this page by googling openssl "2.16.840.1.113894.746875.1.1" :-)
This command creates a PKCS12 with the CA certificate (assuming you already have a CA) which has 2.16.840.1.113894.746875.1.1: <Unsupported tag 6> attribute:
keytool -storepass '***' -import -alias ca -file ***/ca.crt.pem -keystore ***/ca.p12 -deststoretype PKCS12
This command converts the additional PEM-encoded certificate and its private key to PKCS12:
openssl pkcs12 -in ***/additional.cert.pem -inkey ***/additional.key.pem -name additional -export -out ***/additional.p12
This command merges the PKCS12 keystore containing the CA certificate to the PKCS12 keystore containing the additional and its private key:
keytool -importkeystore -srckeystore ***/ca.p12 -srcstoretype pkcs12 -srcstorepass '***' -destkeystore ***/additional.p12 -deststoretype pkcs12 -deststorepass '***'
Comments
Bragolgirith's answer was and (so far) remains correct for the standard cryptoprovider SunJSSE -- a lone cert without a special bag attribute, which OpenSSL doesn't write, is not recognized in Java.
However if you use the BouncyCastle provider, it does not require the bag attribute. In fact for many years it didn't even write the attribute when creating pkcs12; as of 1.71 about 2 years ago it does write it but does not require it when reading. However Bouncy does (write and) require the friendlyname attribute, which SunJSSE writes but does not require, and which openssl does not write by default so you must specify it explicitly. Thus
openssl pkcs12 -export -nokeys -in cert.pem -caname reallyme -out file.p12
creates a file that Java using Bouncy treats as containing a trustedCert. Doing so in a program usually isn't hard, but to get keytool to use Bouncy I haven't found anything simpler than
keytool -list -keystore file.p12 -storetype bcpkcs12 -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/bcprov.jar
Comments
Are you sure the truststore you generated was non-empty?
I've found that running the openssl command that you ran wil generate an empty truststore.
This is confirmed by checking its contents with:
keytool -list -v -keystore truststore.p12
keytoolmay be an easier tool to use.