Call internal APIs having private certificates
Learn how to make HTTPS calls from AEM to web APIs using private or self-signed certificates.
By default when trying to make an HTTPS connection to a web API that uses a self-signed certificate, the connection fails with the error:
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
This issue typically occurs when the API’s SSL certificate is not issued by a recognized certificate authority (CA) and Java™ application cannot validate SSL/TLS certificate.
Let’s learn how to successfully call APIs that have private or self-signed certificates by using Apache HttpClient and AEM’s global TrustStore.
Prototypical API invocation code using HttpClient
The following code makes an HTTPS connection to a web API:
...
String API_ENDPOINT = "https://example.com";
// Create HttpClientBuilder
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
// Create HttpClient
CloseableHttpClient httpClient = httpClientBuilder.build();
// Invoke API
CloseableHttpResponse closeableHttpResponse = httpClient.execute(new HttpGet(API_ENDPOINT));
// Code that reads response code and body from the 'closeableHttpResponse' object
...
The code uses the Apache HttpComponent’s HttpClient library classes and their methods.
HttpClient and load AEM TrustStore material
To call an API endpoint that has private or self-signed certificate, the HttpClient’s SSLContextBuilder
must be loaded with AEM’s TrustStore, and used to facilitate the connection.
Follow the below steps:
-
Login to AEM Author as an administrator.
-
Navigate to AEM Author > Tools > Security > Trust Store, and open the Global Trust Store. If accessing first time, set a password for the Global Trust Store.
-
To import a private certificate, click Select Certificate File button and select desired certificate file with
.cer
extension. Import it by clicking Submit button. -
Update Java™ code like below. Note that to use
@Reference
to get AEM’sKeyStoreService
the calling code must be an OSGi component/service, or a Sling Model (and@OsgiService
is used there).code language-java ... // Get AEM's KeyStoreService reference @Reference private com.adobe.granite.keystore.KeyStoreService keyStoreService; ... // Get AEM TrustStore using KeyStoreService KeyStore aemTrustStore = getAEMTrustStore(keyStoreService, resourceResolver); if (aemTrustStore != null) { // Create SSL Context SSLContextBuilder sslbuilder = new SSLContextBuilder(); // Load AEM TrustStore material into above SSL Context sslbuilder.loadTrustMaterial(aemTrustStore, null); // Create SSL Connection Socket using above SSL Context SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslbuilder.build(), NoopHostnameVerifier.INSTANCE); // Create HttpClientBuilder HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); httpClientBuilder.setSSLSocketFactory(sslsf); // Create HttpClient CloseableHttpClient httpClient = httpClientBuilder.build(); // Invoke API closeableHttpResponse = httpClient.execute(new HttpGet(API_ENDPOINT)); // Code that reads response code and body from the 'closeableHttpResponse' object ... } /** * * Returns the global AEM TrustStore * * @param keyStoreService OOTB OSGi service that makes AEM based KeyStore * operations easy. * @param resourceResolver * @return */ private KeyStore getAEMTrustStore(KeyStoreService keyStoreService, ResourceResolver resourceResolver) { // get AEM TrustStore from the KeyStoreService and ResourceResolver KeyStore aemTrustStore = keyStoreService.getTrustStore(resourceResolver); return aemTrustStore; } ...
- Inject the OOTB
com.adobe.granite.keystore.KeyStoreService
OSGi service into your OSGi component. - Get the global AEM TrustStore using
KeyStoreService
andResourceResolver
, thegetAEMTrustStore(...)
method does that. - Create an object of
SSLContextBuilder
, see Java™ API details. - Load the global AEM TrustStore into
SSLContextBuilder
usingloadTrustMaterial(KeyStore truststore,TrustStrategy trustStrategy)
method. - Pass
null
forTrustStrategy
in above method, it ensures that only AEM trusted certificates succeed during API execution.
- Inject the OOTB
Avoid JVM Keystore changes
A conventional approach to effectively invoke internal APIs with private certificates involves modifying the JVM Keystore. It is achieved by importing the private certificates using the Java™ keytool command.
However, this method is not aligned with security best practices and AEM offers a superior option through the utilization of the Global Trust Store and KeyStoreService.
Solution Package
The sample Node.js project demoed in the video can be downloaded from here.
The AEM servlet code is available in the WKND Sites Project’s tutorial/web-api-invocation
branch, see.