vkuzel.com

Download CA certificate from a server into a Java PKCS #12 key store

2022-04-30

Java can load certificates from different types of key store files. Apart from standard JKS format, the well known PKCS #12 format can be used as well. Files of this format usually have .pfx or .p12 extension.

Goal of this exercise is to download certificate chain from a server, extract a certificate of a certificate authority and create a PKCS #12 file / key store, which can be loaded into a KeyStore implementation.

  1. Print the server certificates to a terminal window.

    openssl s_client -showcerts -connect google.com:443 < /dev/null
    
    • The -showcerts argument ensures that all certificates are printed out, not just a certificate of a server itself.
    • /dev/null input is sent to a server, so the connection is closed.
  2. Copy the PEM data of the certificate into cacert.pem file.

    PEM data are located between "begin" and "end certificate" separators, included. E.g.:

    -----BEGIN CERTIFICATE-----
        MIIOoDCCDYigAwIBAgIQCVam9E4NfB8SeRzeQq7StjANBgkqhkiG9w0BAQsFADBG
        ...
        8vhozb/K3UBYPwfJ8yzidLMs2BuOAf3TuJne60x1K0l17BY3dgm3Sr0y1IDP0j+E
        Yosc8w==
        -----END CERTIFICATE-----
    
  3. Create a key store file, using keytool.

    Use an alias (ASN.1 OID 1.2.840.113549.1.9.20 = friendlyName), so refer to the certificate via the KeyStore API. Without the argument, the keytool will generate random alias.

    keytool -import -alias cacert -file cacert.pem -keystore keystore.p12 -storetype PKCS12
    

    Note: The PKCS #12 files can be created with the opsenssl pkcs12 command as well. But, OpenSSL does not inject all necessary "Bag Attributes" (including alias) into the file. These attributes are required by Java. If you try to use such file, you may encounter the the trustAnchors parameter must be non-empty error.

    Actually, you can debug a key store in the TrustManagerFactoryImpl on a line with the TrustStoreUtil.getTrustedCerts(ks) method call. This call returns all certificates recognised by the key store. If no certificates are returned, the key store file is broken.

  4. Verify the key store file.

    openssl pkcs12 -info -in keystore.p12
    

Load key store into Java

The generated key store file can then be loaded into Java and subsequently used.

For this, you can use standard Java key store implementation, without specifying a provider in the KeyStore.getInstance() method. E.g.:

KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(location.getInputStream(), password.toCharArray());

Or, you can link Bouncy Castle library, and use its more benevolent implementation in terms of loading PKCS #12 files (it can use ASN.1 OID 1.2.840.113549.1.9.21 = localKeyId to identify entry, not just friendlyName like the Java implementation). E.g.:

Security.addProvider(new BouncyCastleProvider());
KeyStore keyStore = KeyStore.getInstance("PKCS12", BouncyCastleProvider.PROVIDER_NAME);
keyStore.load(location.getInputStream(), password.toCharArray());

On the surface, there is no difference. Both implementations are "hidden" behind the java.security.KeyStore API.