Invoking Mutual SSL secured API: WSO2 APIM 3.2.0

You may already aware of the user scenarios based on WSO2 API Manager Mutual SSL which I have addressed here. With this blog post, I’m going to share how we can invoke an API that has been secured by Mutual SSL-Inbound authentication (between the client and APIM).

Source: https://www.freevector.com/vector/ssl-icon

To invoke this kind of APIs, we need to pass the relevant client certificates from the client to GW(APIM) to perform the certificate validation. There are two main options to pass the certificates. 🤘🤘

  1. Pass the certificate directly through the client(ex: cURL, Postman)
  2. Pass the certificate as a Header(ex: in a containerized platform via the LB)

✅ ✅ For both these options, there is a common configuration on the APIM side.

  • Add the client certificate in the ‘Runtime Configurations’ → ‘Transport Level Security’ from the Publisher node.
Upload certificate UI
  • Make sure that if any of the mechanisms have been selected in ‘Runtime Configurations’ → ‘Application Level Security’ for the particular API, those should be configured as Optional(Or else do not select any mechanism Oauth2, Basic, API Key)
Part of Runtime configurations of an API
  • Save the Runtime Configurations and Publish the API.
  • This will add the client certificate to ‘client-truststore.jks’ of the GW node.

✏️ Example command to check the certificate alias:

keytool -v -list -keystore client-truststore.jks | grep "<alias>"
  • And at the same time, an entry will be added to the ‘AM_API_CLIENT_CERTIFICATE’ table of apim_db which having the following format.
CREATE TABLE IF NOT EXISTS AM_API_CLIENT_CERTIFICATE (
TENANT_ID INT(11) NOT NULL,
ALIAS VARCHAR(45) NOT NULL,
API_ID INTEGER NOT NULL,
CERTIFICATE BLOB NOT NULL,
REMOVED BOOLEAN NOT NULL DEFAULT 0,
TIER_NAME VARCHAR (512),
FOREIGN KEY (API_ID) REFERENCES AM_API (API_ID) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (ALIAS,TENANT_ID, REMOVED)
);
  • After this process, you will be to invoke this API. 👊 ✌️

Here comes the invocation options…👇

🚩 Option 1: Pass the certificate directly through the client

Here I will take the Postman as a sample client.

  • Upload the certificate and key to the Postman client by navigating to Settings → Certificate
Click Settings
To add the certificate
After adding the certificate
  • Invoke the API

If the client does not have the relevant certificate or if the relevant certificate is not properly uploaded to ‘Runtime Configurations’ → ‘Transport Level Security’ of the API (hence missing entry in ‘AM_API_CLIENT_CERTIFICATE’ table), the following error message will be thrown.

<ams:fault xmlns:ams="http://wso2.org/apimanager/security">
<ams:code>900901</ams:code>
<ams:message>Invalid Credentials</ams:message>
<ams:description>Invalid Credentials. Make sure you have provided the correct security credentials</ams:description>
</ams:fault>

🚩 Option 2: Pass the certificate as a Header

If the SSL connection between the client and the GW is terminated by the LB/reverse proxy, the following prerequisites need to be met from the LB/reverse proxy.

  • Disable the mutual SSL connection which initializing from the client.
  • Pass the client SSL certificate to the Gateway in an HTTP Header via the LB.

This option involves few configurations to be done on the APIM side. What are those?? 🤔 🤔

  • Add the mutual_ssl configurations in the ‘deployment.toml’ file of the GW nodes. By default, the WSO2 API Manager retrieves the client certificate from the “X-WSO2-CLIENT-CERTIFICATE” header. If needed we can have a custom header as well.
[apimgt.mutual_ssl] 
certificate_header = "<Header Name>"
# This property needs to be true if MutualSSL connection established # between load balancer and gateway.
enable_client_validation = false
#This property needs to be true if the certificate should be decoded # at the GW, when it is passed from the load balancer to the GW in
# encoded manner.
client_certificate_encode = true
  • Invoke the API with the configured ‘certificate_header’ and base64 encoded client certificate.
    Sample commands:
To encode the certificate:
cat domain.crt | base64
To invoke the API:
curl — location — request GET ‘https://localhost:8243/test/1.0/foo' — header ‘accept: applicaition/json’ — header ‘Authorization: Bearer 0ee9aa70-d631–3401-b152–521b431036ca’ — header ‘SSL-CLIENT-CERT: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURsakNDQW40Q0NRRDc2MUpWekluMGNUQU5CZ2txaGtpRzl3MEJBUXNGQURDQmpERUxNQWtHQTFVRUJoTUMKVTB3eEVEQU9CZ05WQkFnTUIxZGxjM1JsY200eEVEQU9CZ05WQkFjTUIwTnZiRzl0WW04eERUQUxCZ05WQkFvTQpCRmRUVHpJeEN6QUpCZ05WQkFzTUFrTlRNUkl3RUFZRFZRUUREQWxzYjJOaGJHaHZjM1F4S1RBbkJna3Foa2lHCjl3MEJDUUVXR25kaGRHaHpZV3hoYTI5eVlXeGxaMlZBWjIxaGFXd3VZMjl0TUI0WERUSXhNREV4TkRBME16VXoKTlZvWERUSXlNREV4TkRBME16VXpOVm93Z1l3eEN6QUpCZ05WQkFZVEFsTk1NUkF3RGdZRFZRUUlEQWRYWlhOMApaWEp1TVJBd0RnWURWUVFIREFkRGIyeHZiV0p2TVEwd0N3WURWUVFLREFSWFUwOHlNUXN3Q1FZRFZRUUxEQUpEClV6RVNNQkFHQTFVRUF3d0piRzlqWVd4b2IzTjBNU2t3SndZSktvWklodmNOQVFrQkZocDNZWFJvYzJGc1lXdHYKY21Gc1pXZGxRR2R0WVdsc0xtTnZiVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQgpBTWMrRjhJblZmMzAwZ2FraTh2QUZ6cUFTSGNQV0xZalQ4dmMwOUs1TzZHRjgzaXpUa2F0UDFtYW1ydWlKL2VRCk1KL2VLVGhJdzR3MWEzS3Y4cjJwc3d2bWRjdjAzZnhRNis2aFh3Ykh5VUtHWFFwbVhtL3d5VE01NzRlR1cybXAKM2toTjlIdFV0SU5uS3BzSENLcFI3MFhGKzNrTTZleHJJNnRJUUpxdTdKM2t1OEdqRVI3R1Vma2trYXI1OGs3eApibEpIWG5URkdjWXJNSXAvcS9YUENqR0pGajhub2tNbjhnL0dWTExCVGFXSWJVa3E2ejRJYjk1dHNOd2thU1dhCnh6U2t3K3JIVkZLWnpPTlV1WTdKTk16Zkp6RkllZG5lY0U3c2Y0WnFIRlF6aUpVbW9qWklDMXp5bFdZdzQ4OEUKNUZvaU9xTWpHYTlUMXhXMUpOWTBab01DQXdFQUFUQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFSZTdrcWZlbwpjd1htazRLWlBKMmlnaGY2VU9jc2dYSEZqMnVpQTNhSWMrd2xwREJpdkdCbDJHM2gxQXl6UFNtcVpYaUcvTGttCkg1dm43VUpGQXlQRVBlQ25HdWduTk5kZGpnSFp0SEdJLzdXcm1LTHdIOEU3TWdmSWJ6dk5Hd3ZXWmRrZi9DblcKNjNDYzhhTzJQMDhYd0dHU25JSDg2cWF6NEtvZUF1aFlCdHZyekNObERraTFjZ1E1bHczU0djU3dxMlB0eEd4cApvS0xWOUJYUzlVdUNJRDRrYjFqRUo3YlplTis0Z0pDbTVGTldUbWdhWXFDcjdERWIwRkhpWitLVnBsZzJZZ3ZYCkM2Z2ZrRm9NYTVJREwvWGVja0J0dFlITzFKcWUyMElRKy9kVHB4ZWE4RjE5aDVmeDRZWVlsRFhLWS8wRmxiRXoKZ1l2UGFIUnVKWnFlV1E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==’ -k

If the configuration is the same as below, we do not want to worry about the certificate encoding. 💁 💁

[apimgt.mutual_ssl]
certificate_header = "<Header Name>"
enable_client_validation = false
client_certificate_encode = false

We can successfully invoke the API with the ‘certificate_header’ with an unencoded certificate that does not include any header, footer, and newlines as below example. 🏆🏆

curl --location --request GET 'https://localhost:8243/test/1.0/foo' --header 'accept: applicaition/json' --header 'Authorization: Bearer 0ee9aa70-d631-3401-b152-521b431036ca' --header 'SSL-CLIENT-CERT: MIIDljCCAn4CCQD761JVzIn0cTANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCU0wxEDAOBgNVBAgMB1dlc3Rlcm4xEDAOBgNVBAcMB0NvbG9tYm8xDTALBgNVBAoMBFdTTzIxCzAJBgNVBAsMAkNTMRIwEAYDVQQDDAlsb2NhbGhvc3QxKTAnBgkqhkiG9w0BCQEWGndhdGhzYWxha29yYWxlZ2VAZ21haWwuY29tMB4XDTIxMDExNDA0MzUzNVoXDTIyMDExNDA0MzUzNVowgYwxCzAJBgNVBAYTAlNMMRAwDgYDVQQIDAdXZXN0ZXJuMRAwDgYDVQQHDAdDb2xvbWJvMQ0wCwYDVQQKDARXU08yMQswCQYDVQQLDAJDUzESMBAGA1UEAwwJbG9jYWxob3N0MSkwJwYJKoZIhvcNAQkBFhp3YXRoc2FsYWtvcmFsZWdlQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMc+F8InVf300gaki8vAFzqASHcPWLYjT8vc09K5O6GF83izTkatP1mamruiJ/eQMJ/eKThIw4w1a3Kv8r2pswvmdcv03fxQ6+6hXwbHyUKGXQpmXm/wyTM574eGW2mp3khN9HtUtINnKpsHCKpR70XF+3kM6exrI6tIQJqu7J3ku8GjER7GUfkkkar58k7xblJHXnTFGcYrMIp/q/XPCjGJFj8nokMn8g/GVLLBTaWIbUkq6z4Ib95tsNwkaSWaxzSkw+rHVFKZzONUuY7JNMzfJzFIednecE7sf4ZqHFQziJUmojZIC1zylWYw488E5FoiOqMjGa9T1xW1JNY0ZoMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEARe7kqfeocwXmk4KZPJ2ighf6UOcsgXHFj2uiA3aIc+wlpDBivGBl2G3h1AyzPSmqZXiG/LkmH5vn7UJFAyPEPeCnGugnNNddjgHZtHGI/7WrmKLwH8E7MgfIbzvNGwvWZdkf/CnW63Cc8aO2P08XwGGSnIH86qaz4KoeAuhYBtvrzCNlDki1cgQ5lw3SGcSwq2PtxGxpoKLV9BXS9UuCID4kb1jEJ7bZeN+4gJCm5FNWTmgaYqCr7DEb0FHiZ+KVplg2YgvXC6gfkFoMa5IDL/XeckBttYHO1Jqe20IQ+/dTpxea8F19h5fx4YYYlDXKY/0FlbEzgYvPaHRuJZqeWQ==' -k

❇️ ❇️ ❇️ Additionally to debug the issues, we can enable the debug logs for ‘org.wso2.carbon.apimgt.gateway.handlers.security.authenticator.MutualSSLAuthenticator’ class.

Ex: DEBUG — MutualSSLAuthenticator Mutual SSL authentication has not happened in the transport level for the API admin-test-1.0, hence API invocation is not allowed

Hope this was helpful...🤗🤗 👋

[1] https://apim.docs.wso2.com/en/3.1.0/learn/api-security/api-authentication/secure-apis-using-mutual-ssl/#securing-apis-with-mutual-ssl

Senior Software Engineer @WSO2