In this scenario, the implementation of the mTLS Authentication (Mutual Transport Layer Security Authentication) policy will be explained.

Since two-way TLS authentication will be done, certificates must be created for the client and server.

In a production environment, it is recommended to purchase certificates from a Certificate Authority. However, for testing or demo purposes, using self-signed certificates will suffice. In this article, we will use Java's keytool to generate self-signed certificates.

To run "keytool" commands, you must be in the path where Java is installed or the "bin" directory of Java must be defined in your environment variables.

1.First, the server keystore is created:

keytool -genkey -alias serverkey -keyalg RSA -keysize 2048 -sigalg SHA256withRSA -keystore serverkeystore.p12 -storepass password -dname "CN=apiqa.apinizer.com, OU=ID, O=YourOrg, L=YourCity, S=YourState, C=YourCountry" -ext san=ip:159.146.116.206,dns:apiqa.apinizer.com -storetype PKCS12
CODE

You will need to change the information in the -dname parameter:

- CN: Your fully qualified server name (Fully Qualified Domain Name - FQDN)
- OU: Your organizational unit (optional)
- O: Your organization (optional)
- L: Your city (optional)
- Q: Your state or province (optional)
- C: Your country (optional)

This information is the information that will appear on your certificate and their accuracy is important.

The keytool -ext option is used to set Subject Alternative Names (SAN) to identify the local hostname/IP address that identifies the server. Generally, more than one address can be specified with this option. However, clients will be limited to using one of these addresses to connect to the server.

SAN (Subject Alternative Name) is often used so that an SSL/TLS certificate can protect multiple domains or IP addresses. Whether you need this information depends on the situation. You need this information if you want your SSL/TLS certificate to protect multiple domains or IP addresses. However, if you only want your certificate to protect a specific domain or IP address, you don't need this information.

2.Then the certificate is imported into the server-certificate.pem file:

keytool -exportcert -keystore serverkeystore.pfx -alias serverkey -storepass password -rfc -file server-certificate.pem
CODE


3.The server certificate is added to the client's trust store:

keytool -import -trustcacerts -file server-certificate.pem -keypass password -storepass password -keystore clienttruststore.pfx -deststoretype pkcs12
CODE


4.Similarly, the client keystore is created:

keytool -genkey -alias clientkey -keyalg RSA -keysize 2048 -sigalg SHA256withRSA -keystore clientkeystore.pfx -storepass password -dname "CN=user2" -ext san=ip:127.0.0.1,dns:localhost
CODE


5.The client certificate is exported:

keytool -exportcert -keystore clientkeystore.pfx -alias clientkey -storepass password -rfc -file client-certificate.pem
CODE


6.The client's certificate is added to the server trust store:

keytool -import -trustcacerts -file client-certificate.pem -keypass password -storepass password -keystore servertruststore.pfx -deststoretype pkcs12
CODE


7.To validate the client's certificate in Apinizer, the user is added to the trust store:

keytool -import -trustcacerts -file client-certificate.pem -keypass password -storepass password -keystore usertruststore.pfx -deststoretype pkcs12
CODE


8.Finally, the files we have:

serverkeystore.pfx -> It is used in the environment keystore.
servertruststore.pfx -> It is used in the environment truststore.
usertruststore.pfx -> Used in credential mTLS settings.
clientkeystore.pfx, clienttruststore.pfx -> Used in client code.

The .p12 and .pfx extensions essentially denote the same file format - PKCS#12. PKCS#12 is a file format that usually contains a server certificate, associated private key, and optionally any intermediate certificates.

.pfx is usually used on Windows platform while .p12 is usually used on Unix or Linux based systems. However, there is no difference between these two file extensions and you don't need to do anything to convert one extension to another.

  • The following command can be used to convert the p12 format to jks:
keytool -importkeystore -srckeystore serverkeystore.pfx -srcstoretype pkcs12 -destkeystore serverkeystore.jks -deststoretype jks
CODE
  • The following command can be used to convert jks format to pfx/p12:
keytool -importkeystore -srckeystore serverkeystore.jks -srcstoretype jks -destkeystore serverkeystore.pfx -deststoretype pkcs12
CODE

Uploading Created User Truststore to Apinizer

Click the Create button on the Key Stores screen.


Click on the New Key Store Definition button on the screen that opens.


The created usertruststore.pfx file is selected and necessary information is entered. Click the Save button.


The client keystore is saved by clicking the Save and Deploy button.


Making Necessary Settings on Apinizer Environments

In order to use the mTLS policy, SSL Offloading must be done on Apinizer Environment's and the mTLS option must be selected.

For this, the HTTPS Enabled option and the mTLS options that come with it are activated on the deployment settings of the Gateway Environment.


The created serverkeystore.pfx and servertruststore.pfx files are added. Password information is entered. Click the Save button.


Republish is required after saving.


Adding mTLS Authentication Policy to API Proxy

Click the Add Policy link on the Develop tab of API Proxy.


mTLS is selected from the policies.


Policy settings are made as follows and the Save button is clicked.


Click mTLS Authentication for detailed information about mTLS Settings.


Credential's mTLS Settings

The username information of the credential must be the same as the "cn name" information in the "issuer" value in the certificate owned by the client.

API Proxy with the mTLS policy added should be added to the API Proxy ACL tab of the credential.

The truststore is added to the mTLS Settings tab of the credential, as in the image below:


Making Requests to API Proxy

API Proxy can be requested with the following client Java code sample and its pom file.

Example Java Code:

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.security.KeyStore;

public class MTLSClientTest {
    public static void main(String[] args) throws Exception {


        KeyStore keystore = KeyStore.getInstance("PKCS12");
        keystore.load(new FileInputStream("<your_path>/clientkeystore.pfx"), "<your_password>".toCharArray());
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keystore, "<your_password>".toCharArray());

        KeyStore truststore = KeyStore.getInstance("PKCS12");
        truststore.load(new FileInputStream("<your_path>/clienttruststore.pfx"), "<your_password>".toCharArray());
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(truststore);

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext);

        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(sslConnectionSocketFactory)
                .build();

        HttpGet httpGet = new HttpGet("https://159.146.116.206:30082/apigateway/demo/pet/pet/findByStatus?status=sold");


        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String responseBody = EntityUtils.toString(entity);
                System.out.println(responseBody);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            httpClient.close();
        }

    }
}
CODE


Pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>Jose-SenderReceiver</groupId>
    <artifactId>Jose-SenderReceiver</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>7</source>
                    <target>7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>com.nimbusds</groupId>
            <artifactId>nimbus-jose-jwt</artifactId>
            <version>9.25.6</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15to18</artifactId>
            <version>1.69</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-text</artifactId>
            <version>1.8</version>
        </dependency>
        <!-- Apache HttpClient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.13</version>
        </dependency>
        <!-- SSLContext for mTLS -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore-nio</artifactId>
            <version>4.4.13</version>
        </dependency>
        <!-- BouncyCastle for mTLS -->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15to18</artifactId>
            <version>1.69</version>
        </dependency>
    </dependencies>
</project>
CODE

In this example, instead of using the apiqa.demo.com address, the reason for providing direct IP access is to manage the test from beginning to end with the certificates we will create/create in the article.

Since cloudflare settings are enabled on the Apinizer site, if the DNS name is used directly, the certificates that cloudflare added must be used.