Bu senaryoda mTLS Kimlik Doğrulama (Mutual Transport Layer Security Authentication) politikasının uygulanması anlatılacaktır.

Çift yönlü TLS kimlik doğrulaması (two-way TLS authentication) yapılacağı için, istemci (client) ve sunucu (server) için sertifikalar oluşturulması gerekmektedir.

Bir üretim ortamında, sertifikaların bir Sertifika Otoritesinden satın alınması önerilir. Ancak, test etmek veya demo amaçlı olarak, kendi imzaladığımız sertifikaları kullanmak yeterli olacaktır. Bu makalede, kendi imzaladığımız sertifikaları oluşturmak için Java'nın keytool'unu kullanacağız.

"keytool" komutlarını çalıştırmak için Java'nın yüklü olduğu path içinde olmanız veya Java'nın "bin" dizininin ortam değişkenlerinizde tanımlı olması gerekir gerekir.

1.Öncelikle, sunucu anahtar deposu oluşturulur:

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

-dname parametresindeki bilgileri değiştirmeniz gerekecektir:

- CN: Tam nitelikli sunucu adınız (Fully Qualified Domain Name - FQDN)
- OU: Organizasyon biriminiz (opsiyonel)
- O: Organizasyonunuz (opsiyonel)
- L: Şehriniz (opsiyonel)
- S: Eyaletiniz veya vilayetiniz (opsiyonel)
- C: Ülkeniz (opsiyonel)

Bu bilgiler sertifikanızda görünecek bilgilerdir ve doğrulukları önemlidir.

Sunucuyu tanımlayan local hostname/IP adresini tanımlamak üzere Subject Alternative Names'i (SAN) ayarlamak için keytool -ext seçeneği kullanılır. Genel olarak bu seçenek ile birden fazla adres belirtilebilir. Ancak, istemciler sunucuya bağlanmak için bu adreslerden birini kullanmakla sınırlandırılacaktır.

SAN (Subject Alternative Name), genellikle bir SSL/TLS sertifikasının birden fazla alan adını veya IP adresini koruyabilmesi için kullanılır. Bu bilgiye ihtiyaç duyup duymadığınız duruma bağlıdır. Eğer SSL/TLS sertifikanızın birden çok alan adını veya IP adresini korumasını istiyorsanız, bu bilgiye ihtiyaç duyarsınız. Ancak, sertifikanızın sadece belirli bir alan adını veya IP adresini korumasını istiyorsanız, bu bilgiye ihtiyaç duymazsınız.

2.Daha sonra, sertifika server-certificate.pem dosyasına aktarılır:

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


3.Sunucu sertifikası istemcinin güven deposuna eklenir:

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


4.Benzer şekilde, istemci anahtar deposu oluşturulur:

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.İstemci sertifikası dışa aktarılır:

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


6.İstemcinin sertifikası sunucu güven deposuna eklenir:

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


7.İstemcinin sertifikasının Apinizer'da doğrulanması için kullanıcı güven deposuna eklenir:

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


8.Son olarak elimizdeki dosyalar:

serverkeystore.pfx -> Environment keystore kısmında kullanılır.
servertruststore.pfx -> Environment truststore kısmında kullanılır.
usertruststore.pfx -> Credential mTLS ayarlarında kullanılır.
clientkeystore.pfx, clienttruststore.pfx -> client kodunda kullanılır.

.p12 ve .pfx uzantıları, aslında aynı dosya formatını belirtir - PKCS#12. PKCS#12, genellikle bir sunucu sertifikası, ilişkili özel anahtar ve isteğe bağlı olarak herhangi bir ara sertifikaları içeren bir dosya formatıdır.

.pfx genellikle Windows platformunda kullanılırken, .p12 genellikle Unix veya Linux tabanlı sistemlerde kullanılır. Ancak, bu iki dosya uzantısı arasında bir fark yoktur ve bir uzantıyı diğerine dönüştürmek için herhangi bir işlem yapmanız gerekmez.

  • p12 formatının jks'ye çevrimi için aşağıdaki komut kullanılabilir:
keytool -importkeystore -srckeystore serverkeystore.pfx -srcstoretype pkcs12 -destkeystore serverkeystore.jks -deststoretype jks
CODE
  • jks formatının pfx/p12'ye çevrimi için aşağıdaki komut kullanılabilir:
keytool -importkeystore -srckeystore serverkeystore.jks -srcstoretype jks -destkeystore serverkeystore.pfx -deststoretype pkcs12
CODE

Oluşturulan User Truststore'un Apinizer'a Yüklenmesi

Key Stores ekranından Oluştur (Create) butonuna tıklanır.


Açılan ekrandan Yeni Key Store Tanımı (New Key Store Definition) butonuna tıklanır.


Oluşturulan usertruststore.pfx dosyası seçilir ve gerekli bilgiler girildikten sonra Kaydet (Save) butonuna tıklanır.


Kaydet ve Yükle (Save and Deploy) butonuna tıklanarak istemci keystore kaydedilmiş olur.


Apinizer Environments Üzerinde Gerekli Ayarların Yapılması

mTLS politikasının kullanılabilmesi için Apinizer Environment's üzerinde SSL Offloading yapılmalı ve mTLS seçeneği seçili olmalıdır.

Bunun için Gateway Environment'ın deployment ayarları üzerinde HTTPS Enabled seçeneği ve onunla birlikte gelen mTLS seçenekleri aktifleştirilir.


Oluşturulan serverkeystore.pfx ve servertruststore.pfx dosyaları eklenir. Parola bilgileri girilir. Kaydet (Save) butonuna tıklanır.


Kaydettikten sonra Yeniden Yayınlama (Republish) gerekmektedir.


API Proxy'ye mTLS Kimlik Doğrulama Politikasının Eklenmesi

API Proxy'nin Geliştirme (Develop) sekmesinden Politika Ekle (Add Policy) linkine tıklanır.


Politikalardan mTLS seçilir.


Politika ayarları aşağıdaki gibi yapılır ve Kaydet (Save) butonuna tıklanır.


mTLS Ayarları hakkında detaylı bilgi için tıklayınız.


Kimlik Bilgisi'nin mTLS Ayarları

Kimlik bilgisinin kullanıcı adı bilgisi, istemcinin sahip olduğu sertifika içerisinde yer alan "issuer" değerindeki "cn name" bilgisi ile aynı olmalıdır.

Kimlik bilgisinin API Proxy ACL sekmesine, mTLS politikası eklenmiş olan API Proxy eklenmelidir.

Kimlik bilgisinin mTLS Ayarları sekmesine aşağıdaki görselde olduğu gibi truststore eklenir:


API Proxy'ye İstek Yapılması

API Proxy'ye aşağıdaki istemci Java kod örneği ve ona ait pom dosyası ile istek yapılabilir.

Örnek Java Kodu:

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

Bu örnekte apiqa.demo.com adresini kullanmak yerine direkt IP üzerinden erişim sağlanmasının sebebi, makalede bizim oluşturacağımız/oluşturduğumuz sertifikalar ile testi baştan sona yönetmektir.

Apinizer sitesi üzerinde cloudflare ayarları etkin olduğundan eğer DNS adı doğrudan kullanılır ise cloudflare'in eklediği sertifikaların kullanılması gerekir.