Two-way SSL authentication client implementation DXP cloud

Calling external API having two way SSL authentication and applying custom SSL to DXP cloud

What is two-way ssl communication?

  • In two-ways ssl authentication, Client and server need to validate and authenticate each other's identities. This can be summarized with the picture below.

  • How ever, I will be writing a separate post about SSL and two-way authentication later.

 

 

 

  In this post we will talk about calling external API which is two way SSL protected and in this case our application will be treated as client. So the API call will be initiated from Liferay and destination should identify our application's identity and validate using certificate and key provided along with the https call. In order to achieve this Application SSL certificate needs to be put into the destination server's trust store and if required the server needs to whitelist the application IP as well. 

In this blog we assume server side IP whitelisting is done and Liferay(Client)’s SSL has been placed in the server’s trust store. 

I am considering our application is hosted on DXP cloud and we have to apply custom SSL.

Applying custom domain to DXP cloud

And run the following command

openssl base64 -in originalkeyfile.key -out base64keyfile.key

openssl base64 -in originalcertfile.crt -out base64certfile.crt

Will encode both the .crt and .key file to base 64 and two new file will be generated.

Now we will copy the content of these file and put into webserver’s LCP.json

Note: we have to remove new lines from the key and crt.

Calling API from java code

Before calling the server API we need to convert the serverCertificate to .p12 and need to upload it to some folder in DXP cloud instance. So that we can fetch it from that folder and pass it from the code while calling API.

  • Run the following command to convert it into .p12 at the same place where we have placed certificate and key

openssl pkcs12 -export -out server.p12 -inkey server.key -in server.crt

  • Keep the password “changeit” by default or note it down somewhere as it needs to be passed as well from java code.

  • Keep in mind server.key and server.crt are original files not the encoded one. There should be a .p12 file created which will be copied to some folder in DXP cloud in this case it is "ssl_certs".

  • We will upload it to dxp cloned workspace in liferay/config/<env>/ssl_certs/keystore.p12 and commit this to repository.

 

  • While creating new container after deployment, every time this file will be copied to opt/liferay/ssl_certs folder and in code we can pick “.p12” file from this location and send it along with https call.

Use below java code to call the API from your custom module.

char[] passphrase = "changeit".toCharArray(); //password

KeyStore ks = KeyStore.getInstance("PKCS12");

FileInputStream fis = new FileInputStream(“/opt/liferay/ssl_cert/keystore.p12”);

ks.load(fis, "changeit".toCharArray());

KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");

kmf.init(ks, "changeit".toCharArray());

SSLContext sc = SSLContext.getInstance("TLS");

sc.init(kmf.getKeyManagers(), null, null);

 

URL url = new URL(“<Server calling API URL>”);

httpsURLConnection = (HttpsURLConnection) url.openConnection();

httpsURLConnection.setRequestMethod("POST");

httpsURLConnection.setRequestProperty("Accept", "*/*");

httpsURLConnection.setRequestProperty("Connection", "keep-alive");

httpsURLConnection.setRequestProperty("apikey", “<if required>”);

httpsURLConnection.setSSLSocketFactory(sc.getSocketFactory());

String encoding = Base64.getEncoder().encodeToString((<USERNAME>+":"+<PASSWORD>).getBytes("UTF-8"));

httpsURLConnection.setRequestProperty("Authorization", "Basic"+encoding);

httpsURLConnection.setDoOutput(true);

String postData = requestJson.toString();

httpsURLConnection.setRequestProperty("Content-Type", "application/json");

OutputStream os = httpsURLConnection.getOutputStream();

_log.info("feedbackPostData:" + postData);

os.write(postData.getBytes());

ins = httpsURLConnection.getInputStream();

isr = new InputStreamReader(ins);

in = new BufferedReader(isr);

String inputLine;

String jsonResponse = "";

while ((inputLine = in.readLine()) != null) {

jsonResponse += inputLine;

}

in.close();

jsonOutput = JSONFactoryUtil.createJSONObject(jsonResponse);

 

  • jsonOutput is the response got from API.

  • You can pass clientID, secretID,API authentication Username and password as required.

1