Mutual TLS authentication simulation with openssl

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

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]#

Search on LinuxDataHub

Leave a Comment