How to Set up mTLS Between Client Java SE Application and Remote OCI API Gateway

Ivan Delić
7 min readMay 3, 2022

mTLS is a powerful approach to secure communication between remote APIs and applications over the zero-trust networks. mTLS ensures all parties symmetrically authenticate themselves with the private key and, at the same time, validate remote parties with their public keys. Zero-trust network? You probably have a zero-trust network if you have a remote cloud backend and some distant on-premise components calling those APIs over the internet. In that case, you would like to limit the API surface to selected client applications only. A possible solution is to use mTLS. mTLS is very resilient, and since it uses TLS certificates, it is an up-and-coming technique. It works best in conjunction with other authorization and authentication techniques, such as OpenID Connect on top of mTLS.

Follow the guide, and you will learn how to make a secure connection between the simple console Java application and remote OCI API Gateway using mTLS.

Assumptions and Constraints

  1. You are familiar with Oracle Cloud Infrastructure, OCI CLI, API Gateway, and Certificates.
  2. You have generated both client (CN=client.ivandelic.com)and server (CN=api.ivandelic.com) TLS certificates under the existing custom Certificate Authority in OCI.
  3. You have provisioned API Gateway and configured at least one deployment with one route.
  4. You have created a DNS record (e.g. api.ivandelic.com) under your custom zone, pointing to the API Gateway public IP address. You can find the public IP address in OCI Console under the API Gateway details.
  5. The example uses Java 8 and relies upon HttpsURLConnection and SSLSocketFactory for HTTP outbound connections towards API Gateway.

Set Up Gateway to Use Server Certificate

In assumption 4, we preassumed the existence of a DNS record pointing to the API Gateway public IP address. To have SSL enabled, you need to match the DNS record (e.g., api.ivandelic.com) with the corresponding server certificate (e.g., CN=api.ivandelic.com), defined in assumption 2, and install that certificate on the API Gateway. Observe that the CN attribute contains your API Gateway's DNS record (hostname), enabling successful SSL validation. It’s time to configure your API Gateway to use a server TLS certificate. Get to API Gateway details following the link or selecting Main Menu > Developer Services > API Management > Gateways. Press the Edit button and change Certificate from Default to predefined server certificate in assumption 2.

Changing TLS certificate of API Gateway
Changing TLS certificate of API Gateway

Configure Deployment to Accept Client Certificate

In assumption 3, we assume you have created at least one Deployment. A deployment is a group of API resources with belonging backends. It is a corresponding mTLS partner with the client Java application. We need to limit the API surface by explaining to the Deployment which client TLS certificate is acceptable from the Java application. We will do that by configuring and accepting CN=client.ivandelic.com inside your Deployment. Get to Deployment details by selecting Main Menu > Developer Services > API Management > Gateways > Your Gateway > Deployments. Select your Deployment and press the Edit button. Select Enable mTLS checkbox and specify the appropriate CN value below. In my case, value is client.ivandelic.com, while you will adjust the value to the CN you generated as a a part of assumption 2.

Enabling mTLS for Deployment

Locate and Prepare Necessary OCIDs

Find and make a note of API Gateway OCID from the OCI Console. You can get API Gateway details by following the link or selecting Main Menu > Developer Services > API Management > Gateways. You will choose your gateway and make a note of the APIGatewayOCID.

Reading API Gateway OCID

Find the client TLS certificate and make a note of the certificate OCID. You can get certificate details by following the link or selecting Main Menu > Identity & Security> Certificates > Certificates. You will select your client certificate and make a note of the ClientOCID.

Reading certificate OCID

Find the server TLS certificate and make a note of the certificate OCID. You can get certificate details by following the link or selecting Main Menu > Identity & Security> Certificates > Certificates. You will choose your server certificate and make a note of the ServerOCID.

Alternatively, you can find ServerOCID by executing the below statement. Replace the placeholder <ocid1.apigateway…> with APIGatewayOCID:

oci api-gateway gateway get --gateway-id <ocid1.apigateway...>

The command will return the JSON containing the key certificate-id. Make a note of ServerOCID.

Construct Java TrustStore

The client application needs to verify the authenticity of the API Gateway with a self-signed CA. For that reason, we construct a trust store containing the CA certificate used on API Gateway. The first set is to extract the certificates used in API Gateway and import the CA certificate into the Java TrustStore. Execute the following command and replace the placeholder <ocid1.certificate…> with ServerOCID:

oci certificates certificate-bundle get --certificate-id <ocid1.certificate...> --bundle-type CERTIFICATE_CONTENT_PUBLIC_ONLY

You will get a JSON response containing server and CA certificates:

{
"data": {
"cert-chain-pem": "-----BEGIN CERTIFICATE-----\...",
"certificate-pem": "-----BEGIN CERTIFICATE-----\...",
"serial-number": "...",
"version-name": null,
"version-number": 1
}
}

Copy the value of cert-chain-pem and paste it in your favorite IDE, without the quotations, of course. You will need to replace newline characters \n with actual newlines. Save the file as server-ca.pem. The file will be similar to:

-----BEGIN CERTIFICATE-----
MIIFjjRCBHytAwIBAgIVAO4eFhVo521pIdDNk/vfrF8xanVlBQ0GCSqGSIb3DQEB
...
S039rocc/MIIFjjRCBHytAwIBAgIVAO4eFhVo521pIdDNk/ommG8Q==
-----END CERTIFICATE-----

Now you need to convert the certificate from the PEM to DER format:

openssl x509 -outform der -in server-ca.pem -out server-ca.der

Once you have the DER certificate ready, it’s time to create the keystone and import it to it:

keytool -import -alias server-ca -keystore server-ca.jks -file server-ca.der

Keytool will ask you to set the password. Pick one and note it since it will protect your new key store. You will use it later from the Java application.

Construct Java KeyStore

To authenticate the client application against the API Gateway, the application needs a private key stored in the key store. Execute the following command and replace the placeholder <ocid1.certificate…> with ClientOCID:

oci certificates certificate-bundle get --certificate-id <ocid1.certificate...> --bundle-type CERTIFICATE_CONTENT_WITH_PRIVATE_KEY

You will get a JSON response containing client and CA certificates, together with the key in PEM format:

{
"data": {
"cert-chain-pem": "-----BEGIN CERTIFICATE-----...\n",
"certificate-pem": "-----BEGIN CERTIFICATE-----...\n",
"private-key-pem": "-----BEGIN PRIVATE KEY-----...\n",
"version-name": null,
"version-number": 1
}
}

Copy the value of cert-chain-pem and paste it in your favorite IDE, without the quotations, of course. You will need to replace newline characters \n with actual newlines. Save the file as client-ca.pem. Copy the value of certificate-pem and paste it into your favorite IDE, replacing characters \n with actual newlines. Save the file as client-cert.pem. Finally, copy the value of private-key-pem and paste it into your favorite IDE, replacing characters \n with actual newlines. Save the file as client-key.pem.

Combine all three certificates into a single file:

cat client-cert.pem client-ca.pem client-key.pem > client-bundle.pem

The file will be similar to:

-----BEGIN CERTIFICATE-----
MIIFjjRCBHytAwIBAgIVAO4eFhVo521pIdDNk/vfrF8xanVlBQ0GCSqGSIb3DQEB
...
S039rocc/MIIFjjRCBHytAwIBAgIVAO4eFhVo521pIdDNk/ommG8Q==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFjjRCBHytAwIBAgIVAO4eFhVo521pIdDNk/vfrF8xanVlBQ0GCSqGSIb3DQEB
...
1esIBv6y6H+aNqN4dztFGS039rocc/MIIFjjRCBHytAwIBAgIVAO4eFhVo521=
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIIFjjRCBHytAwIBAgIVAO4eFhVo521pIdDNk/vfrF8xanVlBQ0GCSqGSIb3DQEB
...
S039rocc/MIIFjjRCBHytAw==
-----END PRIVATE KEY-----

Now you need to create a PKCS Keystore containing a private key:

openssl pkcs12 -export -in client-bundle.pem -out client-bundle.p12 -name client-bundle

Finally, we make the JKS Keystore:

keytool -importkeystore -srckeystore client-bundle.p12 -srcstoretype pkcs12 -destkeystore client-bundle.jks

Building Java Application

Let's build a simple DemoMtls class with the main method. From the main method, we use HttpsURLConnection and SSLSocketFactory for HTTP outbound connections towards API Gateway. Change the API Gateway URL new URL(“https://api.ivandelic.com/v1/hi") accordingly to your instance.

Build the source (e.g., with maven package) and call it from the console. Please remember, since we created the trust and key store, we need to pass them as arguments to the JVM. The correct parameters are:

-Djavax.net.ssl.keyStore=path/to/client-bundle.jks
-Djavax.net.ssl.trustStore=path/to/server-ca.jks
-Djavax.net.ssl.keyStorePassword=changeme
-Djavax.net.ssl.trustStorePassword=changeme

Now call the application:

java -Djavax.net.ssl.keyStore=path/to/client-bundle.jks -Djavax.net.ssl.keyStorePassword=changeme -Djavax.net.ssl.trustStore=path/to/server-ca.jks -Djavax.net.ssl.trustStorePassword=changeme -jar ./target/demo-mtls.jar

Next Steps

Since HttpsUrlConnection is a part of Java SE, you can easily make a port of HttpsUrlConnection to any microservice framework, such as Helidon, Micronaut, Spring, or Quarkus, but also Jakarta EE servers. Also, starting with JDK 11, Java provides a new API for performing HTTP requests, which replaces HttpUrlConnection in a non-blocking async way.

--

--

Ivan Delić

I’m a solution engineer, developer, and architect with strong technical and social skills. My specialties are application development, architecture, and cloud.