Table of Contents
TLS is a protocol designed for authentication and encryption of data for communication over the wire. For TLS to work, the client have to validate the certificate provided by the server, to check whether the server is legitimate. MTLS is a protocol, where both server's authenticity and client's authenticity is checked by client and server respectively. In this article, we will see a practical simulation of MTLS with both successful and failed scenarios.
Aim of this article is to showcase the real time simulation of MTLS connection. All most all the articles and youtube tutorials, only says about successful scenarios and they are using same CA to sign both server and client certificates. It is important to simulate negative scenarios, for better understanding of the topic. The failure scenario mentioned in this article, you will get to know the end to end behavior and working of mtls
Create CA Certificate
We need to create CA for signing our Certificate Signing Request of server and client. For this article, we will be using two CAs (first_ca, second_ca) This is needed for simulating successful and failed scenarios. There is another article, which explains how to create CAs. We will be re using the already created CAs.
Below is my CA directory structure. You can
[root@3-vcp LDH]# tree . . ├── first_ca │ ├── myCA_firstCert.pem │ ├── myCA_firstCert.srl │ └── myCA_first.key ├── second_ca ├── myCA_SecondCert.pem ├── myCA_SecondCert.srl └── mySecondCA.key 2 directories, 6 files
Scenario -1 [ Client & Server Signed with same CA ]
Create Server Certificate
Create private key for Certificate Signing Request
We need to create a private key for creating CSR
[root@3-vcp server]# openssl genrsa -out server_CSRPrivate.key 2048 Generating RSA private key, 2048 bit long modulus ..............+++ ..........................................+++ e is 65537 (0x10001)
Create CSR for Server
We will be creating CSR with the private key created in the above step
[root@3-vcp server]# openssl req -new -key server_CSRPrivate.key -out ServerCSRRequest.csr You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [XX]:IN State or Province Name (full name) []:Karnataka Locality Name (eg, city) [Default City]:Banglore Organization Name (eg, company) [Default Company Ltd]:LDH-buro Organizational Unit Name (eg, section) []:LDH-review-offic Common Name (eg, your name or your server's hostname) []:LDH-review Email Address []:[email protected] Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
Creating Server certificate
We can create the server certificate by signing the CSR with the Certificate Authority certificate. We will using the CA from directory first_ca(one of the two Available CA)
[root@3-vcp server]# openssl x509 -req -in ServerCSRRequest.csr -CA ../first_ca/myCA_firstCert.pem -CAkey ../first_ca/myCA_first.key -CAcreateserial -out Server_First_SignedCertificate.crt -days 365 -sha256 Signature ok subject=/C=IN/ST=Karnataka/L=Banglore/O=LDH-buro/OU=LDH-review-offic/CN=LDH-review/[email protected] Getting CA Private Key
Verify the Content of the Signed Certificate
We can view the content of the certificate to view the CA authority details
[root@3-vcp server]# openssl x509 -noout -text -in Server_First_SignedCertificate.crt Certificate: Data: Version: 1 (0x0) Serial Number: 98:0b:e3:dd:64:a9:31:09 Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, ST=Utah, L=Salt_Lake_City, O=Utah-ca-auth, OU=Utah-ca-section, CN=Utah-Linux-Data-Hub/[email protected] Validity Not Before: Aug 2 14:00:47 2022 GMT Not After : Aug 2 14:00:47 2023 GMT Subject: C=IN, ST=Karnataka, L=Banglore, O=LDH-buro, OU=LDH-review-offic, CN=LDH-review/[email protected] Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit)
Running a Test Server with the generated Certs
We will be creating a web server, for our testing. I have created a python script for the same. The script takes server cert, server key, ca cert , host name as argument. We will be listening on the port 443. It is assumed that this port is enabled in the firewall
The server is running with server cert and key and is running with the same ca which we used to sign the ca (first_ca)
import http.server, ssl, os
import sys
# absolute path of pyServer.py
thisScriptPath=os.path.dirname(os.path.abspath(__file__))+'/'
# starts server on provided host and port
def startServer(host,port):
server_address = (host, port)
httpd = http.server.HTTPServer(server_address, http.server.SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket(httpd.socket,
server_side=True,
certfile=sys.argv[1],
keyfile=sys.argv[2],
ca_certs=sys.argv[3],
cert_reqs=ssl.CERT_REQUIRED,
ssl_version=ssl.PROTOCOL_TLSv1_2
)
print("File Server started at https://" + server_address[0]+":"+str(server_address[1]))
httpd.serve_forever()
# entry point of script
def main():
#print(sys.argv[1])
try:
#generate_selfsigned_cert()
# you can change the host and port
startServer(sys.argv[4],443)
except KeyboardInterrupt:
print("\nFile Server Stopped!")
# call to main function
main()
[root@3-vcp LDH]# python3 server.py server__with_first_ca/Server_First_SignedCertificate.crt server__with_first_ca/server_CSRPrivate.key ./first_ca/myCA_firstCert.pem LDH-review-server
File Server started at https://LDH-review-server:443
Create Client Certificate
Create private key for Certificate Signing Request
We will need to create a private key for CSR.
[root@test-re Client]# openssl genrsa -out client_CSRPrivate.key 2048 Generating RSA private key, 2048 bit long modulus ...........+++ ........................................................................................+++ e is 65537 (0x10001)
Creating Client CSR
We will be creating CSR , which will get signed by the CA Authority. Pls note the common name given in this CSR
[root@test-re Client]# openssl req -new -key client_CSRPrivate.key -out Client_CSRRequest.csr You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [XX]:UG State or Province Name (full name) []:Uganda Locality Name (eg, city) [Default City]:Naira Organization Name (eg, company) [Default Company Ltd]:LinuxDataHub-Uganda Organizational Unit Name (eg, section) []:LDH-syndicate Common Name (eg, your name or your server's hostname) []:LDH-review-client Email Address []:[email protected] Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
Create Client Certificate.
We will using the same CA which we have used for signing the server. (Server and client cert signed by same ca.)(server and client) running on same ca (first_ca)
[root@test-re Client]# openssl x509 -req -in Client_CSRRequest.csr -CA ../first_ca/myCA_firstCert.pem -CAkey ../first_ca/myCA_first.key -CAcreateserial -out Client_First_SignedCertificate.crt -days 365 -sha256 Signature ok subject=/C=UG/ST=Uganda/L=Naira/O=LinuxDataHub-Uganda/OU=LDH-syndicate/CN=LDH-review-client/[email protected] Getting CA Private Key
curl: (51) Unable to communicate securely with peer: requested domain name does not match the server's certificate.
Verify the connection
Connection got established, since both the server and client was able to validate the certificate authenticity. Server used the CA with him to verify the authenticity of the in client Certificate. Same goes for the client also
[root@test-re Client]# curl https://LDH-review:443 --cert ./Client_First_SignedCertificate.crt --key ./client_CSRPrivate.key --cacert ../first_ca/myCA_firstCert.pem
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href="first_ca/">first_ca/</a></li>
<li><a href="second_ca/">second_ca/</a></li>
<li><a href="server.py">server.py</a></li>
<li><a href="server__with_first_ca/">server__with_first_ca/</a></li>
<li><a href="test.py">test.py</a></li>
</ul>
<hr>
</body>
</html>
[Failure] scenario #2 [Client and server having different CA]
In Scenario 1, we have signed both the certificate with same CA. But in this case we will signing the Server with a different ca. So server will be signed by second CA (second_ca) and client will be signed by first_ca
In this scenario, we will be reusing the same CSR (Client and Server) , CAs. I'm skipping those steps, in order to prevent the repetition .
Creating Server certificate
Since I'm reusing the same CSR, we will be signing the CSR with the second CA (second_ca). The CA is already created and is available to us
[root@3-vcp server__with_seond_ca]# openssl x509 -req -in ServerCSRRequest.csr -CA ../second_ca/myCA_SecondCert.pem -CAkey ../second_ca/mySecondCA.key -CAcreateserial -out Server_First_SignedCertificate.crt -days 365 -sha256 Signature ok subject=/C=IN/ST=Karnataka/L=Banglore/O=LDH-buro/OU=LDH-review-offic/CN=LDH-review/[email protected] Getting CA Private Key Enter pass phrase for ../second_ca/mySecondCA.key: [root@3-vcp server__with_seond_ca]#
Running the Server with the new CA and Server key
We have started the server with cert signed by second_ca and we are giving second ca in the server for checking the authenticity of the client certificate.
[root@3-vcp LDH]# python3 server.py server__with_seond_ca/Server_Second_SignedCertificate.crt server__with_seond_ca/server_CSRPrivate.key second_ca/myCA_SecondCert.pem LDH-review-server File Server started at https://LDH-review-server:443
Verifying the Client connection.
Since our requirement is to Sign the client with first CA (first_ca). We will be reusing the client cert from scenario 1, since we have already signed for first scenario . This verification is divided into two parts, Trial 1 and Trial 2
Trial 1
The connection failed since the server is signed by second ca (second_ca), and we are not giving that second_ca certificate in the curl command. Because of this client is not able to validate the authenticity of the server
[root@test-re Client]# curl https://LDH-review:443 --cert ./Client_First_SignedCertificate.crt --key ./client_CSRPrivate.key --cacert ../first_ca/myCA_firstCert.pem curl: (60) Peer's certificate issuer has been marked as not trusted by the user. More details here: http://curl.haxx.se/docs/sslcerts.html curl performs SSL certificate verification by default, using a "bundle" of Certificate Authority (CA) public keys (CA certs). If the default bundle file isn't adequate, you can specify an alternate file using the --cacert option. If this HTTPS server uses a certificate signed by a CA represented in the bundle, the certificate verification probably failed due to a problem with the certificate (it might be expired, or the name might not match the domain name in the URL). If you'd like to turn off curl's verification of the certificate, use the -k (or --insecure) option.
Trial2:
We are going to connect with the second_ca certificate in the curl command. But it can seen that the connection got rejected. This is because , even though the client validate the servers authenticity, the server rejected the clients request. This is because, clients certs are signed by first_ca. But server is not having this ca with him, Only second_ca is known to the server
root@test-re Client]# curl https://LDH-review:443 --cert ./Client_First_SignedCertificate.crt --key ./client_CSRPrivate.key --cacert ../second_ca/myCA_SecondCert.pem curl: (35) Peer does not recognize and trust the CA that issued your certificate. [root@test-re Client]#
We can see that Trial 1 failed since client failed to validate the authenticity of cert shared by the server, Trial 2 failed since server failed to validate the authenticity of cert shared by client
[success] scenario #3 [Client and server having different CA]
In this scenario, we will be using the existing generated server and client certs. The server is signed by second_ca and client is signed with first_ca. And in the server side we have first_ca
Running the Server
Server is running with certificate signed by second_ca and is having first_ca for checking the authenticity of the client certificate.
[root@3-vcp LDH]# python3 server.py server__with_seond_ca/Server_Second_SignedCertificate.crt server__with_seond_ca/server_CSRPrivate.key first_ca/myCA_firstCert.pem LDH-review-server
File Server started at https://LDH-review-server:443
Verifying the connection
We will be using curl command with client certificate signed by first_ca and ca cert of second_ca to validate the authenticity of certificate offered by the server.
It can be seen that the connection is successful
[root@test-re Client]# curl https://LDH-review:443 --cert ./Client_First_SignedCertificate.crt --key ./client_CSRPrivate.key --cacert ../second_ca/myCA_SecondCert.pem <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Directory listing for /</title> </head> <body> <h1>Directory listing for /</h1> <hr> <ul> <li><a href="first_ca/">first_ca/</a></li> <li><a href="second_ca/">second_ca/</a></li> <li><a href="server.py">server.py</a></li> <li><a href="server__with_first_ca/">server__with_first_ca/</a></li> <li><a href="server__with_seond_ca/">server__with_seond_ca/</a></li> <li><a href="test.py">test.py</a></li> </ul> <hr> </body> </html>
We can see client [with second_ca] was able to validate the cert [signed by second_ca] shared by the server, and server [with first_ca] was able to validate the cert [signed by first_ca] shared by client
Complete Directory Structure
Below is the complete directory structure which is refered as relative path in this article
At server side
[root@3-vcp LDH]# tree . . ├── first_ca │ ├── myCA_firstCert.pem │ ├── myCA_firstCert.srl │ └── myCA_first.key ├── second_ca │ ├── myCA_SecondCert.pem │ ├── myCA_SecondCert.srl │ └── mySecondCA.key ├── server.py ├── server__with_first_ca │ ├── server_CSRPrivate.key │ ├── ServerCSRRequest.csr │ └── Server_First_SignedCertificate.crt ├── server__with_seond_ca │ ├── server_CSRPrivate.key │ ├── ServerCSRRequest.csr │ ├── Server_First_SignedCertificate.crt │ └── Server_Second_SignedCertificate.crt └── test.py 4 directories, 16 files
At Client Side
We have copied the CAs from server to the client machine
[root@test-re LDH]# tree . . ├── Client │ ├── client_CSRPrivate.key │ ├── Client_CSRRequest.csr │ └── Client_First_SignedCertificate.crt ├── first_ca │ ├── myCA_firstCert.pem │ ├── myCA_firstCert.srl │ └── myCA_first.key └── second_ca ├── myCA_SecondCert.pem ├── myCA_SecondCert.srl └── mySecondCA.key 3 directories, 9 files [root@test-re LDH]#