In a previous post, I discussed some general topics relating to the usage of HTTPS and certificates within Oracle Public Cloud. In this follow up piece, I will work through a concrete example and explain how to set up a Java Cloud Service instance in such a way that Integration Cloud Service can consume a service deployed to that platform over HTTPS.
The use case we have in mind here is a simple one. A custom REST-based service is deployed to WebLogic on JCS (I'll use a simple Servlet that returns a JSON payload). An integration defined in Integration Cloud Service uses the REST adaptor to invoke that service over HTTPS. Since JCS is an example of a compute-based PaaS service, it is provisioned by default without an external hostname and with a self-signed certificate mapped to the Load Balancer IP Address. This is different to the ICS instance, which is accessible via an *.oraclecloud.com hostname with an automatically-trusted certificate. The first thing we will do is configure JCS to use a hostname that we provide, rather than the IP address. We'll then look at how to provision a certificate for that instance and then finally, how to configure ICS.
I've used a JCS instance based on WebLogic 12.1.3 and Oracle Traffic Director 184.108.40.206 for this post. Exact steps may differ somewhat for other versions of the service.
I've deployed my simple Servlet to WebLogic via the console and for now, the only option available to me is to access it via the IP address of the JCS Load Balancer. We can see from the screenshots below that my web browser first prompts me to accept the self-signed certificate before accessing the end point, which is not what we want to happen:
I've added a DNS entry mapping that IP (220.127.116.11) to an A record within my domain as below:
And I also add this hostname (jcs-lb.securityateam.org.uk) in the OTD console on my JCS instance:
I can now access the service with the hostname, but the certificate issue remains:
We need to configure our JCS instance with a certificate that matches our hostname. There are two options:
This option is preferable for production as the configuration of clients is far simpler. There is, generally, a cost associated, though. I've opted to use a trial certificate and have performed the following steps:
The first step (which is the same for either option) is to generate a Certificate Signing Request. When we do this, OTD generates a keypair and includes the public key in the request, which is to be sent to a CA for signing. Note how we use the hostname of our server as the common name (CN) in the request.
I copy the CSR and paste in to the CA website and obtain my certificate, which is emailed to me once issued.
Along with the server certificate itself, I receive a number of root and intermediate CA certificates, which I install into OTD as CA Certificates before importing my new server certificate.
I deployed the configuration and restarted OTD (just to be safe), before copying the Base64-encoded server certificate I was sent and importing that into OTD.
The last step here is to modify my HTTPS listener in OTD to use my new certificate, as below. Once that is done, I can successfully connect to the server over SSL using my hostname.
Many organisations that use TLS certificates widely for internal communication security will have their own in-house Certificate Authority. These organisations have weighed up the costs and benefits and decided that it makes more sense to sign all of their server certificates in house - and to deal with the pain of configuring clients to manually trust this CA - than it does to buy a certificate for each server.
Most of the configuration steps from a JCS/OTD perspective are the same. I am going to use my colleague Chris Johnson's simple yet awesome Simple CA script to create a self-signed CA certificate. I create a certificate signing request from within the OTD console as before, and then use an openssl command like the below to create the certificate based on my CSR.
openssl x509 -req -in server-cert.csr -out server-cert.cer -CA ca.crt -CAkey ca.key -CAcreateserial -CAserial ca.serial -days 365 -sha256
That command uses the CSR I created (which I saved as "server-cert.csr") and generates a certificate, signed by the CA certificate ("ca.crt") created by Chris's script. The output is in "server-cert.cer" and I can validate the contents as below:
Now I repeat the steps above; first importing the self-signed CA certificate into OTD as a trusted certificate, then importing my server certificate and finally updating my listener to use the new certificate.
One important change, though, is that I can no longer hit the REST endpoint directly with my browser, since, once again, the "Unknown Issuer" exception prevents my browser from establishing a secure connection. Because the CA cert that signed my server certificate is not trusted by the browser, I need to manually import this certificate into the browser trust store before I can access the URL.
Within our Integration Cloud Service console, we're going to create a new Connection to our REST end-point on JCS. The steps that we need to follow will depend on which of the two options above we've gone with. Let's do the simpler one first.
ICS ships with a set of pre-configured trusted CA certificates, as you can see here:
As long as the SSL certificate that you have installed in your JCS instance has been signed by one of the pre-configured trusted CA's in this list, then you don't need to do anything more in order to configure the HTTPS connection using the ICS REST Adapter.
I've now changed my OTD listener back to the certificate signed by the self-signed CA. Here's what happens when I test the connection in JCS:
The error message is a rather familiar one, especially to those who are used to configuring Java environments to connect to un-trusted certificates:
Unable to test connection "JCSREST_ROTTOTEST". [Cause: CASDK-0003]: - CASDK-0003: Unable to parse the resource, https://jcs-lb.securityateam.org.uk/simple/users. Verify that URL is reachable, can be parsed and credentials if required are accurate - sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target - PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target - unable to find valid certification path to requested target
This is, in fact, exactly the same exception I get when using a simple Java test client to connect to that end-point:
Fortunately, the fix is quite simple. All I need to do is to manually import the self-signed CA cert into ICS as a trusted issuer and I can then successfully connect to the REST endpoint.
Once I perform the above step, I am able to successfully connect from ICS to JCS once more.