All Products
Search
Document Center

API Gateway:HTTPS mutual authentication

Last Updated:Dec 10, 2025

In mutual authentication, both the client and the server must verify each other's identity to establish an HTTPS connection. This process adds several steps to the handshake compared with one-way authentication. In one-way authentication, a client establishes a secure channel by downloading and validating the server's public key certificate. In mutual authentication, the client also sends its public key certificate to the server for validation. Data transmission begins only after the client and server successfully authenticate each other.

How it works

One-way authentication process

In a one-way authentication process, the server stores a public key certificate and a private key. The handshake process is as follows:

  1. The client initiates an HTTPS connection request. It sends the supported SSL/TLS protocol versions to the server.

  2. The server responds and sends its public key certificate (server.crt).

  3. The client validates the server certificate (server.crt) and extracts the server's public key.

  4. The client generates a random number R as a pre-master secret. It encrypts the pre-master secret with the server's public key and sends the ciphertext to the server.

  5. The server uses its private key (server.key) to decrypt the ciphertext and recover the pre-master secret R.

  6. Both parties use the pre-master secret to negotiate and generate a shared session key. All subsequent data is encrypted with this symmetric key.

Mutual authentication process

  1. The client initiates an HTTPS connection request. It sends the supported SSL/TLS protocol versions to the server.

  2. The server responds and sends its public key certificate (server.crt).

  3. The client validates the server certificate (server.crt) and extracts the server's public key.

  4. The client sends its public key certificate (client.crt) to the server.

  5. The server uses a trusted root certificate (root.crt) to validate the client certificate and extract the client's public key.

  6. The client sends its list of supported cipher suites to the server.

  7. The server negotiates the best encryption scheme based on the algorithms that both parties support. It then encrypts the encryption parameters with the client's public key and sends them to the client.

  8. The client uses its private key to decrypt the encryption parameters. It generates a random number R as a pre-master secret, encrypts it with the server's public key, and sends it to the server.

  9. The server uses its private key to decrypt and obtain the random number R.

  10. Both parties use the pre-master secret to generate a session key. All subsequent data is encrypted with this symmetric key for transmission.

1. Prepare certificates

For mutual authentication, you need the following six certificate files:

  • Server public key certificate: server.crt

  • Server private key file: server.key

  • Root certificate: root.crt

  • Client public key certificate: client.crt

  • Client private key file: client.key

  • Client integrated certificate (includes public and private keys for browser access): client.p12

You can request all certificates from a certification authority (CA), which usually involves a fee. We recommend that you choose a major CA for this purpose. If the certificates are for internal use only, you can also issue self-signed certificates. For more information, see Self-signed certificates.

2. Configure HTTPS mutual authentication in API Gateway

After you prepare the six certificate files, you can configure API Gateway to enable HTTPS mutual authentication by binding the server certificate and root certificate to a domain name.

  1. Log on to the API Gateway console. In the navigation pane on the left, choose API Management > Groups. On the Groups page, click Create Group in the upper-right corner.

  2. On the Create Group page, select an Instance, enter a Group Name and BasePath, and then click OK.

  3. On the Groups page, click the group that you created to open the Group Details page.

  4. In the Independent Domain Names section of the group details page, click Bind Domain Name.

  5. In the Bind Domain Name dialog box, enter the Domain Name.

  6. After you bind the domain name, click the Select Certificate link in the domain name's row.

  7. In the Select Certificate dialog box, click the Add Certificate Manually link.

  8. In the Create Certificate dialog box, add the three certificates mentioned in Prepare certificates:

    • Paste the content of the server public key certificate (server.crt) into the Certificate Content text box.

    • Paste the content of the server private key file (server.key) into the Private Key text box.

    • Paste the content of the root certificate (root.crt) into the Root Certificate text box.

After you complete these steps, HTTPS mutual authentication is configured in API Gateway.

3. Self-signed certificates

Before you generate the certificates, you must create a self-signed root certificate. This root certificate is used to issue the server and client public key certificates. To verify the logic of certificate issuance and authentication, use the root certificate to generate two different sets of client certificates. Then, use both client certificates to send requests and check whether the server correctly identifies and processes both.

3.1 Generate a self-signed root certificate

  1. Create a private key for the root certificate.

    openssl genrsa -out root.key 2048
  2. Create a certificate signing request (CSR) file for the root certificate.

    openssl req -new -out root.csr -key root.key

    Enter the following parameters as needed. The following code provides an example:

    Country Name (2 letter code) [XX]:cn
    State or Province Name (full name) []:bj
    Locality Name (eg, city) [Default City]:bj
    Organization Name (eg, company) [Default Company Ltd]:alibaba
    Organizational Unit Name (eg, section) []:test
    Common Name (eg, your name or your servers hostname) []:root
    Email Address []:a.alibaba.com
    A challenge password []:
    An optional company name []:
  3. Create the root certificate.

    openssl x509 -req -in root.csr -out root.crt -signkey root.key -CAcreateserial -days 3650
    Important

    The Common Name for the root certificate must be `root`. The Common Name for the client and server certificates must be the corresponding domain name. The Common Name for the root certificate cannot be the same as the Common Name for the client or server certificates. The other fields must be consistent across the root, server, and client certificates. When prompted for a password during generation, you can press Enter to skip it.

These three commands generate a root certificate, root.crt, with a 10-year validity period. You can use this root certificate to issue server and client certificates.

3.2 Generate a self-signed server certificate

  1. Generate a private key for the server certificate.

    openssl genrsa -out server.key 2048
  2. Generate a CSR file for the server certificate. For more information about the procedure and items to note, see the previous section.

    openssl req -new -out server.csr -key server.key
  3. Generate the server public key certificate.

    openssl x509 -req -in server.csr -out server.crt -signkey server.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650

After you run these three commands, the following files are generated:

  • server.key: The private key file for the server.

  • server.crt: The server public key certificate with a 10-year validity period. It is generated using the root certificate and the server private key file.

3.3 Generate a self-signed client certificate

  1. Generate a key for the client certificate.

    openssl genrsa -out client.key 2048
    openssl genrsa -out client2.key 2048
  2. Generate a CSR file for the client certificate. For more information about the procedure and items to note, see the previous section.

    openssl req -new -out client.csr -key client.key
    openssl req -new -out client2.csr -key client2.key
  3. Generate the client certificate.

    openssl x509 -req -in client.csr -out client.crt -signkey client.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650
    openssl x509 -req -in client2.csr -out client2.crt -signkey client2.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650
  4. Generate a client certificate in P12 format. You must enter a password, such as 123456.

    openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
    openssl pkcs12 -export -clcerts -in client2.crt -inkey client2.key -out client2.p12

By repeating the commands, you can generate two sets of client certificates:

- client.key / client2.key: The private key files for the clients.

- client.crt / client2.key: A client certificate valid for 10 years.

Use the root certificate and the client private key to generate client.p12 and client2.p12. This certificate file contains the client's public and private keys and is used for browser access.

4. Verification

Use curl and specify the certificate paths to test whether HTTPS mutual authentication is configured correctly. The following three use cases are tested:

  • Use the client.crt / client.key certificate set to call the server.

  • Use the client2.crt / client2.key certificate set to call the server.

  • Call the server without a certificate.

The following sections show the test results for the three use cases.

4.1 Successful calls with certificates

Use the client.crt / client.key certificate set to call the server.

# --cert specifies the path of the client public key certificate.
# --key specifies the path of the client private key file.
# -k Use this parameter to skip certificate validation because we are using a self-signed certificate.
# You can use -v to observe the SSL handshake process.
curl --cert ./client.crt --key ./client.key https://integration-fred2.fredhuang.com -k -v
* Rebuilt URL to: https://47.93.XX.XX/
*   Trying 47.93.XX.XX...
* TCP_NODELAY set
* Connected to 47.93.XX.XX (47.93.XX.XX) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: C=CN; ST=BJ; L=BJ; O=Alibaba; OU=Test; CN=integration-fred2.fredhuang.com; emailAddress=a@alibaba.com
*  start date: Nov  2 01:01:34 2019 GMT
*  expire date: Oct 30 01:01:34 2029 GMT
*  issuer: C=CN; ST=BJ; L=BJ; O=Alibaba; OU=Test; CN=root; emailAddress=a@alibaba.com
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET / HTTP/1.1
> host:integration-fred2.fredhuang.com
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.17.5
< Date: Sat, 02 Nov 2019 02:39:43 GMT
< Content-Type: text/html
< Content-Length: 612
< Last-Modified: Wed, 30 Oct 2019 11:29:45 GMT
< Connection: keep-alive
< ETag: "5db97429-264"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p>Thank you for using nginx.</p>
</body>
</html>
* Connection #0 to host 47.93.XX.XX left intact

Use the client2.crt / client2.key certificate set to call the server.

curl --cert ./client2.crt --key ./client2.key https://integration-fred2.fredhuang.com -k
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p>Thank you for using nginx.</p>
</body>
</html>

4.2 Calls without certificates

curl https://integration-fred2.fredhuang.com -k
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/1.17.5</center>
</body>
</html>

All three use cases work as expected. The client validates the server certificate and uploads its own certificate to the server for validation. Both client certificates issued by the root certificate can successfully initiate HTTPS mutual authentication requests. Requests made without a client certificate are denied by the server.

5. Use Java to call

Because a self-signed certificate is used, you must add the server certificate to the trusted certificate store to make a successful call with ApacheHttpClient. Alternatively, you can configure the code to ignore certificate validation.

cd $JAVA_HOME
sudo ./bin/keytool -import -alias ttt -keystore cacerts -file /Users/fred/temp/cert5/server.crt

After you add the server public key certificate to the trusted certificate store, you can use the following code to initiate an HTTPS request with a client certificate:

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.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
public class HttpClientWithClientCert {
    private final static String PFX_PATH = "/Users/fred/temp/cert5/client.p12";    // Path of the client certificate.
    private final static String PFX_PWD = "123456";    // Password of the client certificate.
    public static String sslRequestGet(String url) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        InputStream instream = new FileInputStream(new File(PFX_PATH));
        try {
            keyStore.load(instream, PFX_PWD.toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, PFX_PWD.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext
                , new String[] { "TLSv1" }    // supportedProtocols. Set this as needed.
                , null    // supportedCipherSuites
                , SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        try {
            HttpGet httpget = new HttpGet(url);
            //httpget.addHeader("host", "integration-fred2.fredhuang.com");// Set headers.
            CloseableHttpResponse response = httpclient.execute(httpget);
            try {
                HttpEntity entity = response.getEntity();
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");// Return the result.
                EntityUtils.consume(entity);
                return jsonStr;
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
    public static void main(String[] args) throws Exception {
        System.out.println(System.getProperty("java.home"));
        System.out.println(sslRequestGet("https://integration-fred2.fredhuang.com"));
    }
}