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.
-
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.
- The
-
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-----
-
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 theKeyStore
API. Without the argument, thekeytool
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 thethe trustAnchors parameter must be non-empty
error.Actually, you can debug a key store in the
TrustManagerFactoryImpl
on a line with theTrustStoreUtil.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. -
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.