15

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:

  1. Create a keypair + certificate using openssl.
  2. 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.)

asked Mar 13, 2017 at 15:06
3
  • 3
    Truststores usually do not cointain private keys. May be that is confusing Java? remove the private key if not needed or add the certificate without private key a second time. Commented Mar 13, 2017 at 15:42
  • i tried your snipped and it worked using openssl 0.9.8zh and oracle jdk 1.8.0_121, it's strange Commented Mar 13, 2017 at 15:47
  • keytool may be an easier tool to use. Commented Mar 13, 2017 at 16:13

5 Answers 5

19

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:

answered Mar 22, 2017 at 18:06
Sign up to request clarification or add additional context in comments.

4 Comments

I am on "1.8.0_171" and this magic attribute is no longer required. All entries added to PKCS12 store are trusted when used in a trust manager.
I am on "1.8.0_171" and this is not working, PKCS12 containing only certificates give the "the trustAnchors parameter must be non-empty" error
I am on "11.0.14.1" and PKCS12 files containing only certificates are still not properly loaded.
How can we add this magic attribute?
8

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 ""
answered Dec 21, 2024 at 0:11

Comments

8

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 '***'
answered Dec 14, 2021 at 11:02

Comments

2

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
answered Feb 14, 2024 at 20:44

Comments

0

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
answered Feb 14, 2024 at 16:47

1 Comment

Assuming you mean the last command (pkcs12 -export -nokeys) the file is NOT empty, but Java (or rather SunJSSE) wrongly treats it as empty, as thoroughly explained seven years ago, so your keytool command doesn't show the contents.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.