This is a topic that has taken me quite some time to figure out. There are bits and pieces of information scattered and one has to put everything together. I was hoping that with this post I could help others quickly assemble a working solution.
I have a client-cert.pem
, client-key.pem
and a root.pem
files and I need to used them in my Java client to access a remote REST API.
How do I package them into a truststore and use them to make API calls?
Advertisement
Answer
In order to load your certificates into your application your will need to package them into a truststore.
Creating a truststore
given the 3 files:
client-cert.pem
client-key.pem
root.pem
Run the following commands in your terminal. Replace PASSWORD
with your desired password.
Package your client key and certificate into a keystore. This will create a PKCS12 keystore file.
openssl pkcs12 -export -inkey client-key.pem -in client-cert.pem -out client.pfx -passout pass:PASSWORD -name qlikClient
Add the keystore to your truststore. It will create a truststore if the destination doesn’t exit. This will create a
PKCS12
truststore file. By default it creates aJKS
file which is a proprietary format. By specifying-deststoretype PKCS12
you will create a file which is in an industry standard format.keytool -importkeystore -destkeystore truststore.pfx -deststoretype PKCS12 -deststorepass PASSWORD -srckeystore client.pfx -srcstorepass PASSWORD -srcstoretype PKCS12 -alias qlikClient
Add your root CA to the truststore
keytool -importcert -keystore truststore.pfx -storepass PASSWORD -file root.pem -noprompt -alias qlikServerCACert
Note that in the above commands we use the same PASSWORD
for both the keystore and the truststore. You could alternatively use different passwords. Also note that you have to specify an alias for each item you add to the truststore.
If you want your truststore to trust all cacerts available in your system add -trustcacerts
option to step 2 or 3.
You can use the following command to list the contents of your truststore
keytool -list -keystore truststore.pfx -storepass PASSWORD
Using the truststore in you application
Once you have your truststore you need to load it into your application. Assuming you have a constant KEYSTORE_PATH
holding the path to your truststore and keyStorePass
holding the password, read the truststore file into a KeyStore
private KeyStore readStore() { try (InputStream keyStoreStream = new FileInputStream(KEYSTORE_PATH)) { KeyStore keyStore = KeyStore.getInstance("PKCS12"); // or "JKS" keyStore.load(keyStoreStream, keyStorePass.toCharArray()); return keyStore; } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException e) { throw new RuntimeException(e); } }
Create a custom SSLContext
and a custom HttpClient
,
final KeyStore truststore = readStore(); final SSLContext sslContext; try { sslContext = SSLContexts.custom() .loadTrustMaterial(truststore, new TrustAllStrategy()) .loadKeyMaterial(truststore, keyStorePass.toCharArray(), (aliases, socket) -> "qlikClient") .build(); } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException | UnrecoverableKeyException e) { throw new RuntimeException("Failed to read keystore", e); } final CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build();
You can now use this HttpClient
to make requests to your API.
HttpResponse response = httpClient.execute(new HttpGet("https://sense-gcp-central1eu.net:4242/qrs/app/full"));
Or, if you are using the OpenUnirest/unirest-java library, you can configure Unirest to use your custom HttpClient
Unirest.config().httpClient(httpClient); HttpResponse<JsonNode> response = Unirest.get("https://sense-gcp-central1eu.net:4242/qrs/app/full").asJson();
References