ar_doc_24 openssl_ECC_enc_dec_signature - JohnHau/mis GitHub Wiki

Setup & verify mutual TLS authentication (MTLS) with openssl Table of Contents 1. Overview on SSL and TLS 2. Overview on mTLS 3. Why should we use mutual authentication (MTLS)? 4. Lab Environment 5. Create CA certificate 5.1 Configure openssl.cnf 5.2 Create private key 5.3 Create CA certificate 5.4 Convert certificate to PEM format 6. Create client certificate 6.1 Create private key 6.2 Generate Certificate Signing Request (CSR) 6.3 Add certificate extensions 6.4 Create client certificate 7. Create server certificate 7.1 Create private key 7.2 Create Certificate Signing Request (CSR) 7.3 Add certificate extensions 7.4 Create server certificate 8. Validate mutual TLS authentication Mehtod-1: Using Nodejs Method-2: Using openssl Summary Further Readings In this article we will explore Mutual Transport Layer Security (MTLS) and we will use a client and server setup to quickly validate mTLS authentication. We will use openssl to create the required certificates and verify the mutual TLS authentication.

  1. Overview on SSL and TLS I hope you are already familiar with SSL and TLS. Transport Layer Security (TLS) is a protocol you can use to protect network communications from eavesdropping and other types of attacks. It is an update to the Secure Sockets Layer (SSL) protocol that preceded it, and often people still refer to both collectively as “SSL” or use the terms “SSL” and “TLS” interchangeably. TLS protects traffic at the transport layer so you can wrap a number of higher-level plain-text protocols in TLS to secure them. Some popular examples of protocols that can be wrapped in TLS include HTTP (HTTPS, that lock icon in the URL bar of your browser), FTP (FTPS, not to be confused with SFTP which uses the SSH protocol), IMAP (IMAPS), POP3 (POP3S), and SMTP (SMTPS), among others. As you can see, it’s common to add an “S” at the end of a protocol that is wrapped in SSL or TLS.

TLS provides you with two primary protections.

The first protection, and the one most commonly associated with TLS, is that TLS encrypts your traffic to protect it from eavesdropping. The second and equally important protection is that TLS authenticates the server to the client (and optionally the client to the server as well) with the use of signed certificates.

  1. Overview on mTLS By default TLS only validates the authenticity of the server and not of the client (application) which is sending the request. We can use mutual TLS where both clients request certificates from the server to ensure the server is who it says it is, and the server requests certificates from the client to prove who it is as well. When using mutual authentication, not only does the service side prove its identity by exposing a certificate, but also the clients prove their identity to the servers by exposing a client-side certificate. This provides a higher level of security compared to normal TLS/HTTPS usage, where only the identity of the server is proven. Setting up and maintaining mutual authentication; that is, the provision of new, and the rotating of outdated, certificates, is known to be complex and is therefore seldom used.

  2. Why should we use mutual authentication (MTLS)? Isn't it sufficient to protect external APIs with HTTPS and OAuth 2.0/OIDC access tokens?

As long as the attacks come through the external API, it might be sufficient.

But what if a client node with the application becomes compromised?

For example, if an attacker gains control over the node, then the attacker can start listening to traffic between other nodes in the setup. If the internal communication is sent as plain text, it will be very easy for the attacker to gain access to sensitive information sent between the nodes in the setup. To minimize the damage caused by such an intrusion, mutual authentication can be used to prevent an attacker from eavesdropping on internal network traffic.

  1. Lab Environment I will be using Virtual Machines running on Oracle VirtualBox to demonstrate this article. These VMs are installed with CentOS 7 and 8 Linux. I have two VMs where in one will act as a server while the other will act as a client. I will use the server node to create all the certificates i.e. CA certificate, server and client certificate.

The hostname of the server node is server.example.com with an IP address 192.168.0.114 while the client hostname is server-2.example.com with an IP address of 192.168.0.152

  1. Create CA certificate First we would need a CA certificate which can sign both the client and server certificates. So let's create our directory structure to store the CA certificate and key.

[root@server ~]# mkdir /root/mtls

[root@server ~]# cd /root/mtls/

[root@server mtls]# mkdir certs private Next create an index.txt and serial file to track the list of certificates signed by the CA certificate.

[root@server mtls]# echo 01 > serial [root@server mtls]# touch index.txt

5.1 Configure openssl.cnf I will copy openssl.cnf from /etc/pki/tls/openssl.cnf and then modify it. I have already explained individual section of this file in Configure openssl.cnf for Root CA Certificate.

So here I will skip the explanation, just sharing a copy of my openssl.cnf for reference:

This definition stops the following lines choking if HOME isn't

defined.

HOME = . RANDFILE = $ENV::HOME/.rnd

Extra OBJECT IDENTIFIER info:

#oid_file = $ENV::HOME/.oid oid_section = new_oids

[ new_oids ]

Policies used by the TSA examples.

tsa_policy1 = 1.2.3.4.1 tsa_policy2 = 1.2.3.4.5.6 tsa_policy3 = 1.2.3.4.5.7

#################################################################### [ ca ] default_ca = CA_default # The default ca section

[ CA_default ] dir = /root/mtls # Where everything is kept certs = $dir/certs # Where the issued certs are kept database = $dir/index.txt # database index file. # several certs with same subject. new_certs_dir = $dir/certs # default place for new certs. certificate = $dir/certs/cacert.pem # The CA certificate serial = $dir/serial # The current serial number crlnumber = $dir/crlnumber # the current crl number # must be commented out to leave a V1 CRL private_key = $dir/private/cakey.pem # The private key

name_opt = ca_default # Subject Name options cert_opt = ca_default # Certificate field options

default_days = 365 # how long to certify for default_crl_days= 30 # how long before next CRL default_md = sha256 # use SHA-256 by default preserve = no # keep passed DN ordering policy = policy_match

For the CA policy

[ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional

[ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional

#################################################################### [ req ] default_bits = 2048 default_md = sha256 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca # The extentions to add to the self signed cert

[ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = IN countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = Some-State localityName = Locality Name (eg, city) localityName_default = BANGALORE 0.organizationName = Organization Name (eg, company) 0.organizationName_default = GoLinuxCloud organizationalUnitName = Organizational Unit Name (eg, section) commonName = Common Name (eg, your name or your server's hostname) commonName_max = 64 emailAddress = Email Address emailAddress_max = 64

[ req_attributes ] challengePassword = A challenge password challengePassword_min = 4 challengePassword_max = 20 unstructuredName = An optional company name

[ v3_req ]

Extensions to add to a certificate request

basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ v3_ca ]

Extensions for a typical CA

subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer basicConstraints = critical,CA:true

[ crl_ext ]

issuerAltName=issuer:copy

authorityKeyIdentifier=keyid:always

5.2 Create private key We would need a private key for the CA certificate.

[root@server mtls]# openssl genrsa -out private/cakey.pem 4096 Generating RSA private key, 4096 bit long modulus ..........................................................++ ...........................................++ e is 65537 (0x10001)

5.3 Create CA certificate We will use this private key to generate our CA certificate:

[root@server mtls]# openssl req -new -x509 -days 3650 -config /root/mtls/openssl.cnf -key private/cakey.pem -out certs/cacert.pem Sample output snippet from my terminal:

image

5.4 Convert certificate to PEM format This is an optional step but you can convert the certificate into PEM format:

[root@server mtls]# openssl x509 -in certs/cacert.pem -out certs/cacert.pem -outform PEM

  1. Create client certificate Now we will create the client certificate which will be used by the client node i.e. server-2.example.com in our case. I will use the same node i.e. server.example.com to generate the client certificates. But let me create a different directory to store these certificates:

[root@server mtls]# mkdir /root/client_certs [root@server mtls]# cd /root/client_certs/

6.1 Create private key We will again need a different private key for the client certificate.

[root@server client_certs]# openssl genrsa -out client.key.pem 4096 Generating RSA private key, 4096 bit long modulus ..........................................................................++ ..................................................................++ e is 65537 (0x10001)

6.2 Generate Certificate Signing Request (CSR) ALSO READ: Many people miss most important points when they are creating a CSR. If you are not sure about what should be added for individual fields then I would recommend to read this article before you generate CSR: Things to consider when creating CSR with OpenSSL

Next we need to generate the CSR for the client certificate. For our CA certificate we had given few details such as Country Name. State, Locality etc. If you check the openssl.cnf file, the CA certificate is using the policy_match section for the CSR.

policy = policy_match

For the CA policy

[ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional It is important that any CSR you generate either for client or server certificates, should have matching Country Name, State and Organization Name with the CA certificate or else while signing the certificate you will get error like:

The stateOrProvinceName field needed to be the same in the CA certificate (XXXXX) and the request (YYYYY So with that in mind, let's generate the CSR for the client certificate.

[root@server client_certs]# openssl req -new -key client.key.pem -out client.csr Sample output from my terminal:

image

NOTE: Make sure you give proper Common Name with the hostname of your client node. If you have a scenario where you can access the server using multiple addresses then you should create a SAN certificate.

6.3 Add certificate extensions We will also add some certificate extensions to our client certificate. You may modify or ignore this step based on your requirement. We will create an additional configuration file with the required extensions

[root@server client_certs]# cat client_ext.cnf basicConstraints = CA:FALSE nsCertType = client, email nsComment = "OpenSSL Generated Client Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment extendedKeyUsage = clientAuth, emailProtection

6.4 Create client certificate Next we will create our client certificate:

[root@server client_certs]# openssl ca -config /root/mtls/openssl.cnf -extfile client_ext.cnf -days 1650 -notext -batch -in client.csr -out client.cert.pem Using configuration from /root/mtls/openssl.cnf Check that the request matches the signature Signature ok Certificate Details: Serial Number: 1 (0x1) Validity Not Before: Apr 8 11:43:21 2021 GMT Not After : Oct 14 11:43:21 2025 GMT Subject: countryName = IN stateOrProvinceName = Karnataka organizationName = GoLinuxCloud commonName = server-2 emailAddress = [email protected] X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Cert Type: SSL Client, S/MIME Netscape Comment: OpenSSL Generated Client Certificate X509v3 Subject Key Identifier: EA:CD:99:7F:C4:85:DA:A0:16:EF:1C:61:F1:66:8B:13:CD:0D:38:9A X509v3 Authority Key Identifier: keyid:B2:9D:04:DD:06:02:97:93:FC:08:09:CA:B9:F6:7B:13:1B:BB:74:C5

        X509v3 Key Usage: critical
            Digital Signature, Non Repudiation, Key Encipherment
        X509v3 Extended Key Usage:
            TLS Web Client Authentication, E-mail Protection

Certificate is to be certified until Oct 14 11:43:21 2025 GMT (1650 days)

Write out database with 1 new entries Data Base Updated

The path of the CA certificate required to sign the certificate will be picked from /root/mtls/openssl.cnf. You can verify the same using:

[root@server client_certs]# cat /root/mtls/index.txt V 251014114321Z 01 unknown /C=IN/ST=Karnataka/O=GoLinuxCloud/CN=server-2/emailAddress=[email protected] So this says that a certificate for server-2 has been signed by the root CA certificate.

  1. Create server certificate We will again create a separate directory to store the server certificates.

[root@server client_certs]# mkdir ../server_certs [root@server client_certs]# cd ../server_certs/

7.1 Create private key We need another private key for the server certificate:

[root@server server_certs]# openssl genrsa -out server.key.pem 4096 Generating RSA private key, 4096 bit long modulus ..................++ ...............................................................++ e is 65537 (0x10001)

7.2 Create Certificate Signing Request (CSR) We will follow the same guidelines as I explained while creating CSR for client certificate. The Country, State and Organization Name must match with the CA certificate or else the signing would fail later.

[root@server server_certs]# openssl req -new -key server.key.pem -out server.csr Sample output snippet from my terminal:

image

I have highlighted the Common Name used as it is important that you use the hostname/FQDN which will be used to identify the server. Here in my case the FQDN of my server is server.example.com but I have just given the hostname here as the CN value.

7.3 Add certificate extensions Similar to client certificate, we will again add some extensions to our server certificate. Additionally I am adding Subject Alternative Name field also known as SAN. This is used to define multiple Common Name. In my case it is possible that a client can access the server using IP address or FQDN so I would also like to add those in the certificate.

[root@server server_certs]# cat server_ext.cnf basicConstraints = CA:FALSE nsCertType = server nsComment = "OpenSSL Generated Server Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always keyUsage = critical, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] IP.1 = 192.168.0.114 IP.2 = 10.0.2.15 DNS.1 = server.example.com Since I have two interfaces on the server.example.com, I have given the IP address of both the interface as client can also reach to my server using other interface.

7.4 Create server certificate Now we will use this extension file along with the private key and CSR to generate our server certificate. The path of the CA certificate required to sign the certificate will be picked from /root/mtls/openssl.cnf.

[root@server server_certs]# openssl ca -config /root/mtls/openssl.cnf -extfile server_ext.cnf -days 1650 -notext -batch -in server.csr -out server.cert.pem Using configuration from /root/mtls/openssl.cnf Check that the request matches the signature Signature ok Certificate Details: Serial Number: 2 (0x2) Validity Not Before: Apr 8 11:52:58 2021 GMT Not After : Oct 14 11:52:58 2025 GMT Subject: countryName = IN stateOrProvinceName = Karnataka organizationName = GoLinuxCloud commonName = server emailAddress = [email protected] X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Cert Type: SSL Server Netscape Comment: OpenSSL Generated Server Certificate X509v3 Subject Key Identifier: F4:90:6E:30:54:93:35:04:2F:48:DF:D6:55:C8:35:54:4D:29:E6:7E X509v3 Authority Key Identifier: keyid:B2:9D:04:DD:06:02:97:93:FC:08:09:CA:B9:F6:7B:13:1B:BB:74:C5 DirName:/C=IN/ST=Karnataka/L=BANGALORE/O=GoLinuxCloud/CN=ca-server/emailAddress=[email protected] serial:85:16:E3:D6:C5:59:18:DD

        X509v3 Key Usage: critical
            Digital Signature, Key Encipherment
        X509v3 Extended Key Usage:
            TLS Web Server Authentication
        X509v3 Subject Alternative Name:
            IP Address:192.168.0.114, IP Address:10.0.2.15, DNS:server.example.com

Certificate is to be certified until Oct 14 11:52:58 2025 GMT (1650 days)

Write out database with 1 new entries Data Base Updated Verify the index content of the root CA:

[root@server ~]# cat /root/mtls/index.txt V 251014114321Z 01 unknown /C=IN/ST=Karnataka/O=GoLinuxCloud/CN=server-2/emailAddress=[email protected] V 251014115258Z 02 unknown /C=IN/ST=Karnataka/O=GoLinuxCloud/CN=server/emailAddress=[email protected] So the root CA has also signed another certificate for CN=server.

  1. Validate mutual TLS authentication Now we will verify the mutual TLS authentication between the server and the client node. To achieve this I will copy the client certificates on to the client node under a new directory /root/certs. I have already created this directory on my client node, here 192.168.0.152 is the IP address of my client i.e. server-2.example.com

[root@server server_certs]# scp ../client_certs/* 192.168.0.152:/root/certs/ [email protected]'s password: client.cert.pem 100% 2220 2.7MB/s 00:00 client.csr 100% 1740 2.4MB/s 00:00 client_ext.cnf 100% 290 580.1KB/s 00:00 client.key.pem 100% 3243 4.9MB/s 00:00 We also need to copy the CA certificate over to the client node:

[root@server server_certs]# scp /root/mtls/certs/cacert.pem 192.168.0.152:/root/certs/
[email protected]'s password: cacert.pem 100% 2106 3.5MB/s 00:00 There are multiple methods to verify the mTLS authentication but I will use nodejs and openssl to validate this. You can also use LDAP, Apache etc to verify this in your environment.

Mehtod-1: Using Nodejs In this method I will create a simple nodejs file to setup a web server our HTTPS and then access this web server using the client node over TLS.

First make sure nodejs is installed on your server node which is part of EPEL repo:

[root@server ~]# yum install epel-release -y

[root@server ~]# yum install nodejs -y This will install NodeJS on the server node. Next create a simple webserver application file, here I have provided the path of CA certificate, server certificate and server private key.

[root@server ~]# cat web-server.js const https = require('https'); const fs = require('fs');

const hostname = '192.168.0.114'; const port = 3000;

const options = { ca: fs.readFileSync('/root/mtls/certs/cacert.pem'), cert: fs.readFileSync('/root/server_certs/server.cert.pem'), key: fs.readFileSync('/root/server_certs/server.key.pem'), rejectUnauthorized: true, requestCert: true, };

const server = https.createServer(options, (req, res) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end('Hello World\n'); });

server.listen(port, hostname, () => { console.log(Server running at https://${hostname}:${port}/); });

Start your web server:

[root@server ~]# node web-server.js Server running at https://192.168.0.114:3000/ You can also enable port 3000 in the firewall on the server node:

[root@server ~]# firewall-cmd --add-port=3000/tcp --permanent success

[root@server ~]# firewall-cmd --reload success Now login to your client node and using curl try to access the web server:

[root@server-2 ~]# curl --cacert /root/certs/cacert.pem --key /root/certs/client.key.pem --cert /root/certs/client.cert.pem https://192.168.0.114:3000/ Hello World

So as expected, the client was able to connect to the web server using the client certificate. So this proves the mutual TLS authentication where both server and client are using TLS certificate to prove their identity.

Method-2: Using openssl Let me show you one more method to validate the mutual TLS authentication in Linux using openssl.

On the server node execute the following command:

[root@server ~]# openssl s_server -accept 3000 -CAfile /root/mtls/certs/cacert.pem -cert /root/server_certs/server.cert.pem -key /root/server_certs/server.key.pem -state

Next on the client node execute:

[root@server-2 ~]# openssl s_client -connect 192.168.0.114:3000 -key /root/certs/client.key.pem -cert /root/certs/client.cert.pem -CAfile /root/certs/cacert.pem -state

If everything is correct then you should see a successful TCP handshake between the server and client node.

Sample output from the server node:

[root@server ~]# openssl s_server -accept 3000 -CAfile /root/mtls/certs/cacert.pem -cert /root/server_certs/server.cert.pem -key /root/server_certs/server.key.pem -state Using default temp DH parameters ACCEPT SSL_accept:before/accept initialization SSL_accept:SSLv3 read client hello A SSL_accept:SSLv3 write server hello A SSL_accept:SSLv3 write certificate A SSL_accept:SSLv3 write key exchange A SSL_accept:SSLv3 write server done A SSL_accept:SSLv3 flush data SSL_accept:SSLv3 read client certificate A SSL_accept:SSLv3 read client key exchange A SSL_accept:SSLv3 read certificate verify A SSL_accept:SSLv3 read finished A SSL_accept:SSLv3 write session ticket A SSL_accept:SSLv3 write change cipher spec A SSL_accept:SSLv3 write finished A SSL_accept:SSLv3 flush data -----BEGIN SSL SESSION PARAMETERS----- MFUCAQECAgMDBALAMAQABDCgaykG+1HPMhTTcfScPIt8DoLAv6WrLYRajaqKCL+J TXS6zrWEccetgmLAMZf8pZKhBgIEYG9GgqIEAgIBLKQGBAQBAAAA -----END SSL SESSION PARAMETERS----- Shared ciphers:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:0x07+0x08:0x08+0x08:0x09+0x08:0x04+0x08:0x0A+0x08:0x05+0x08:0x0B+0x08:0x06+0x08:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:RSA+SHA224:ECDSA+SHA1:RSA+SHA1 Shared Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:RSA+SHA224:ECDSA+SHA1:RSA+SHA1 Supported Elliptic Curve Point Formats: uncompressed:ansiX962_compressed_prime:ansiX962_compressed_char2 Supported Elliptic Curves: 0x001D:P-256:0x001E:P-521:P-384 Shared Elliptic curves: P-256:P-521:P-384 CIPHER is ECDHE-RSA-AES256-GCM-SHA384 Secure Renegotiation IS supported

Sample output from the client node:

[root@server-2 ~]# openssl s_client -connect 192.168.0.114:3000 -key /root/certs/client.key.pem -cert /root/certs/client.cert.pem -CAfile /root/certs/cacert.pem -state CONNECTED(00000003) SSL_connect:before SSL initialization SSL_connect:SSLv3/TLS write client hello SSL_connect:SSLv3/TLS write client hello Can't use SSL_get_servername SSL_connect:SSLv3/TLS read server hello depth=1 C = IN, ST = Karnataka, L = BANGALORE, O = GoLinuxCloud, CN = ca-server, emailAddress = [email protected] verify return:1 depth=0 C = IN, ST = Karnataka, O = GoLinuxCloud, CN = server, emailAddress = [email protected] verify return:1 SSL_connect:SSLv3/TLS read server certificate SSL_connect:SSLv3/TLS read server key exchange SSL_connect:SSLv3/TLS read server done SSL_connect:SSLv3/TLS write client key exchange SSL_connect:SSLv3/TLS write change cipher spec SSL_connect:SSLv3/TLS write finished SSL_connect:SSLv3/TLS write finished SSL_connect:SSLv3/TLS read server session ticket SSL_connect:SSLv3/TLS read change cipher spec SSL_connect:SSLv3/TLS read finished

Certificate chain 0 s:C = IN, ST = Karnataka, O = GoLinuxCloud, CN = server, emailAddress = [email protected] i:C = IN, ST = Karnataka, L = BANGALORE, O = GoLinuxCloud, CN = ca-server, emailAddress = [email protected] 1 s:C = IN, ST = Karnataka, L = BANGALORE, O = GoLinuxCloud, CN = ca-server, emailAddress = [email protected] i:C = IN, ST = Karnataka, L = BANGALORE, O = GoLinuxCloud, CN = ca-server, emailAddress = [email protected]

Server certificate -----BEGIN CERTIFICATE----- MIIG+jCCBOKgAwIBAgIBAjANBgkqhkiG9w0BAQsFADCBhzELMAkGA1UEBhMCSU4x EjAQBgNVBAgMCUthcm5hdGFrYTESMBAGA1UEBwwJQkFOR0FMT1JFMRUwEwYDVQQK DAxHb0xpbnV4Q2xvdWQxEjAQBgNVBAMMCWNhLXNlcnZlcjElMCMGCSqGSIb3DQEJ ARYWYWRtaW5AZ29saW51eGNsb3VkLmNvbTAeFw0yMTA0MDgxMTUyNThaFw0yNTEw MTQxMTUyNThaMHAxCzAJBgNVBAYTAklOMRIwEAYDVQQIDAlLYXJuYXRha2ExFTAT r5xznC64Ql9P9Wy3nlSpBwcWwsdrltVaSnGYqZ0BCH6VoepgXhSlryCzKq67tnes DE/26in8Z/MHuPkBTmX6pj1Xi8ugj8MUyqrC1nJ2bKgcTFzgKOjtlT2Z/2QUsdp7 ildSkcczPyI38PaBrJQOaTpz/vGcujKHldpbbJdksaOCAicq+tqPjFVXHSgo2b6L cnAe5hoZhNK03EPIVf4jsluNzUbTxaWy5Ut6fbvR0ZUuTxdgZ7iHb1EhSjKro5OG FQa214LFTHGeFCWq3wty45tw1+BvtnufIJ7HB4SS97qXJmtIsFldl/mvsuCUnvbs 7S2Kee3lRTJ3KTtPxh9QISin+LFE4cXovcdkuYsKM6Gd4tf9RMUKIbHEeY9OgslQ /fpEOr1ev6eH9EQSHXE= -----END CERTIFICATE----- subject=C = IN, ST = Karnataka, O = GoLinuxCloud, CN = server, emailAddress = [email protected]

issuer=C = IN, ST = Karnataka, L = BANGALORE, O = GoLinuxCloud, CN = ca-server, emailAddress = [email protected]


No client certificate CA names sent Peer signing digest: SHA256 Peer signature type: RSA Server Temp Key: ECDH, P-256, 256 bits

SSL handshake has read 4217 bytes and written 415 bytes Verification: OK

New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384 Server public key is 4096 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES256-GCM-SHA384 Session-ID: 7D1BDC137AF9E00707C00FCCFEE195E520FD20EF4C0F0C31BF94E759F3CD0C6E Session-ID-ctx: Master-Key: A06B2906FB51CF3214D371F49C3C8B7C0E82C0BFA5AB2D845A8DAA8A08BF894D74BACEB58471C7AD8262C03197FCA592 PSK identity: None PSK identity hint: None SRP username: None TLS session ticket lifetime hint: 300 (seconds) TLS session ticket: 0000 - 85 20 fa 90 03 0f 80 22-cd ff 8b 35 95 3e a9 a0 . ....."...5.>.. 0010 - ae 10 1a 15 d0 70 ce bc-33 84 e0 c9 aa 74 45 18 .....p..3....tE. 0020 - b3 2c 85 cd 71 9a 0b 6d-7d 2d 9f 66 ce 2d 35 72 .,..q..m}-.f.-5r 0030 - 03 1d b7 21 04 47 b0 ac-ff b1 26 e3 6b d9 f0 23 ...!.G....&.k..# 0040 - 32 2b b6 c8 ce df 8a e1-9d 47 33 f8 57 6d 5b 59 2+.......G3.Wm[Y 0050 - 3b 0f 2b b1 32 00 62 98-14 5f e1 5f 24 94 d9 54 ;.+.2.b.._._$..T 0060 - ae e4 79 46 3d 5e 4a dd-99 13 72 8b c5 12 d3 a6 ..yF=^J...r..... 0070 - 2e 37 60 7f 0e 7d 29 37-0b 36 15 63 4e 10 d4 8c .7`..})7.6.cN... 0080 - 0f c1 ff b5 f2 c2 5b 03-f6 66 99 cb af 81 70 52 ......[..f....pR 0090 - 44 cb 00 61 e8 12 d1 d3-31 ff fb 4d 9e ec 1d 92 D..a....1..M....

Start Time: 1617905283
Timeout   : 7200 (sec)
Verify return code: 0 (ok)
Extended master secret: no

Summary In this tutorial we learned all about Transport Layer Security (MTLS) and how it is different compared to SSL and TLS. We also learned the steps to validate mutual TLS authentication between a server and a client node using openssl and NodeJS. I had written another article to verify similar scenario using Apache server. Now you should be familiar with mTLS and the steps to create and validate certificates.

Further Readings Implementing SSL/TLS Using Cryptography and PKI Mutual Authentication

Related Searches: mtls, mutual tls, mutual authentication, what does TLS mean, tls mutual authentication, what is tls certificate, curl client certificate, what is mtls, what is mutual authentication

Things to consider when creating CSR with OpenSSL Table of Contents Problems which you can face with incorrect CSR Important points to consider when creating CSR Modify default values for CSR (using custom configuration) Self-Signed Certificate CSR Example RootCA Certificate CSR Example Generate CSR for SAN certificate Add custom X.509 extensions to Certificate Summary I have now covered multiple tutorials on working with openssl certificates. But there is one question where I get a lot of questions where certificate doesn't work due to incorrect entries in Certificate Signing Request.

So I have decided to create a dedicated tutorial to explain why CSR is important, and things to consider when writing a CSR.

Problems which you can face with incorrect CSR Writing a CSR is the most crucial part of generating a certificate. If your CSR is not proper then: RootCA may fail to sign the certificate Your MTLS authentication will not work with TCP handshake error You will end up creating multiple certificates for each host if you are not familiar with SAN Your X.509 extensions will not be properly added and many more ..

Important points to consider when creating CSR The openssl command will by default consider /etc/pki/tls/openssl.cnf as the configuration file unless you specify your own configuration file using -config. The req_distinguished_name field is used to get the details which will be asked while generating the CSR. You can alter this section inside the openssl.cnf and add the default values, modify the conditions such as min and max allowed characters etc There are different policy sections available in the openssl.cnf. The policy_anything is normally used for self-signed certificates where all the fields except commonName are optional. The policy_match section is used to generate RootCA certificates. If you are planning to use this RootCA certificate to sign any server or client certificate, then the respective sections marked as match must be same between RootCA and server or client certificate. In case you provide your stateOrProvinceName as Karnataka in RootCA and KARNATAKA in server certificate then the signing will fail as both will be considered as different values. commonName is used for MTLS communications. The commonName must match the HOSTNAME or FQDN of the server on the server certificate and client on the client certificate. So if we have apache-client.example.com sending request to apache-server.example.com with MTLS authentication then the server certificate should have commonName as apache-server.example.com and client certificate should have apache-client.example.com To consider High Availability and Load balancing, in IT organizations we use single FQDN mapped to multiple IP Addresses so in such case we prefer to use SAN certificates. So an -extfile param can be used with openssl command to provide the list of IP Address which would be validated for respective certificate. In such case you can provide the server's domain name as commonName while generating the CSR. NOTE: Since this article is all about Certificate Signing Request, I will not explain other areas of certificate generation. This article assumes that you have necessary environment such as openssl command, private keys etc to generate the CSR

Modify default values for CSR (using custom configuration) Now let us define our own configuration file which will have all the set of default values to be used to generate the CSR. Here is my sample configuration file:

[root@controller certs]# cat custom_openssl.cnf Sample Output:

distinguished_name = req_distinguished_name

[ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = IN countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) ## Print this message stateOrProvinceName_default = KARNATAKA ## This is the default value localityName = Locality Name (eg, city) ## Print this message localityName_default = BANGALORE ## This is the default value 0.organizationName = Organization Name (eg, company) ## Print this message 0.organizationName_default = GoLinuxCloud ## This is the default value organizationalUnitName = Organizational Unit Name (eg, section) ## Print this message organizationalUnitName_default = Admin ## This is the default value commonName = Common Name (eg, your name or your server hostname) ## Print this message
commonName_max = 64 emailAddress = Email Address ## Print this message emailAddress_max = 64 Next let us try to generate CSR using this custom configuration file:

[root@controller certs]# openssl req -new -key server.key -out server.csr -config custom_openssl.cnf 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) [IN]: State or Province Name (full name) [KARNATAKA]: Locality Name (eg, city) [BANGALORE]: Organization Name (eg, company) [GoLinuxCloud]: Organizational Unit Name (eg, section) [Admin]: Common Name (eg, your name or your server's hostname) []:server.example.com ## provide hostname of your localhost Email Address []: ## can be skipped or provide any email address As expected, all the default values have been updated based on our custom configuration. So I can just press ENTER and default values will be considered for the CSR.

Although we need to provide Common Name which should have the server's hostname or FQDN

Self-Signed Certificate CSR Example Let us start with the self-signed certificates first. This is the fastest way to achieve a TLS communication with minimal security as it is better than plain text based communication.

Here is a sample output to generate the CSR for self-signed certificate. Since we have not defined any configuration file, by default openssl will consider /etc/pki/tls/openssl.cnf:

[root@controller certs]# openssl req -new -key server.key -out server.csr


Country Name (2 letter code) [XX]:IN ## By default maximum 2 char allowed State or Province Name (full name) []:ANYTHING ## can be anything Locality Name (eg, city) [Default City]:ANYTHING ## can be anything Organization Name (eg, company) [Default Company Ltd]:ANYTHING ## can be anything Organizational Unit Name (eg, section) []:ANYTHING ## can be anything Common Name (eg, your name or your server hostname) []:server.example.com ## provide the hostname/fqdn of the localhost server node Email Address []:ANYTHING ## can be anything

Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: ## can be anything or leave empty An optional company name []: ## can be anything or leave empty

RootCA Certificate CSR Example We also need to write a CSR when creating our own RootCA certificate. If you are not providing your own openssl.cnf then by default /etc/pki/tls/openssl.cnf will be considered.

Now by default RootCA certificate follows below below guidelines from /etc/pki/tls/openssl.cnf:

policy = policy_match

For the CA policy

[ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional This would mean that both RootCA and server/client certificate must have same countryName, stateOrProvinceName and organizationName or else the signing will fail.

Let us take a look at this with a practical example:

Let me generate my RootCA certificate:

[root@controller certs]# openssl genrsa -out ca.key 4096

[root@controller certs]# openssl req -new -x509 -days 365 -key ca.key -out cacert.pem


Country Name (2 letter code) [XX]:IN State or Province Name (full name) []:KARNATAKA Locality Name (eg, city) [Defaultt City]:BENGALORE Organization Name (eg, company) [Default Company Ltd]:GoLinuxCloud Organizational Unit Name (eg, section) []:Admin Common Name (eg, your name or your server's hostname) []:RootCA Email Address []:

[root@controller certs]# ls -l cacert.pem -rw-r--r-- 1 root root 2049 Aug 28 00:16 cacert.pem Now we will generate one server certificate:

[root@controller certs]# openssl genrsa -out server.key 4096

[root@controller certs]# openssl req -new -key server.key -out server.csr


Country Name (2 letter code) [XX]:IN State or Province Name (full name) []:Karnataka Locality Name (eg, city) [Defaultt City]:Bengaluru Organization Name (eg, company) [Default Company Ltd]:Golinuxcloud Organizational Unit Name (eg, section) []:Dev Common Name (eg, your name or your server's hostname) []:server.example.com Email Address []:

Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:

As you may have noticed, I have provided the different values for countryName, stateOrProvinceName and organizationName for both rootCA and server certificate CSR.

Let us try to now create server certificate by signing this using CSR and rootCA certificate:

[root@controller certs]# openssl ca -days 2650 -notext -batch -create_serial -cert cacert.pem -keyfile ca.key -in server.csr -out cacert.pem Using configuration from /etc/pki/tls/openssl.cnf Check that the request matches the signature Signature ok The stateOrProvinceName field is different between CA certificate (KARNATAKA) and the request (Karnataka)

As expected, the first check itself has failed since the stateOrProvinceName has different CASE even though the content is same hence they are considered different.

But you can still use openssl x509 command to generate server certificate, the difference is that you will not be able to keep a track of different certificates signed by your RootCA:

[root@controller certs]# openssl x509 -req -days 3650 -in server.csr -CA cacert.pem -CAkey ca.key -CAcreateserial -out server.crt Signature ok subject=C = IN, ST = Karnataka, L = Bengaluru, O = Golinuxcloud, OU = Dev, CN = server.example.com Getting CA Private Key Verify the certificate content:

[root@controller certs]# openssl x509 -noout -text -in server.crt | grep -E 'Subject:|Issuer:' Issuer: C = IN, ST = KARNATAKA, L = BENGALORE, O = GoLinuxCloud, OU = Admin, CN = RootCA Subject: C = IN, ST = Karnataka, L = Bengaluru, O = Golinuxcloud, OU = Dev, CN = server.example.com As you can see, the CSR content for both Issuer and Certificate is different and yet you were able to generate the certificate.

Generate CSR for SAN certificate We can create a custom configuration file with the list of SAN details which needs to be added to the CSR:

[root@controller certs]# cat custom_openssl.cnf [req] req_extensions = req_ext distinguished_name = req_distinguished_name

[req_distinguished_name] countryName = Country Name (2 letter code) stateOrProvinceName = State or Province Name (full name) localityName = Locality Name (eg, city) 0.organizationName = Organization Name (eg, company) organizationalUnitName = Organizational Unit Name (eg, section) commonName = Common Name (eg, your name or your server's hostname) emailAddress = Email Address

[req_ext] subjectAltName = @alt_names

[alt_names] IP.1 = 10.10.10.13 IP.2 = 10.10.10.14 IP.3 = 10.10.10.17 DNS.1 = centos8-2.example.com DNS.2 = centos8-3.example.com Under alt_names section you can provide the list of your IP or hostname or FQDN which will be used for the authentication. Now if your certificate will act as a server certificate then you should all the IP details from your server node and if your certificate will act as a client then you should client details.

The TCP handshake will check the SAN field from both server and client certificate when trying to perform an MTLS connection.

Let us generate our CSR using this configuration file:

[root@controller certs]# openssl req -new -newkey rsa:4096 -nodes -out server.csr -config custom_openssl.cnf Generating a RSA private key .......++++ ......................... ......................................................++++ writing new private key to stdout -----BEGIN PRIVATE KEY----- MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQC9oU7pOjTF60sd 60EwhsLCaYyUg7dExVDfBt1GwbSRYvk/LphTXck7K8rlmeJv7ECOk/2ju9Ad5wXl

...

Country Name (2 letter code) []:IN State or Province Name (full name) []:Karnataka Locality Name (eg, city) []:Bengaluru Organization Name (eg, company) []:GoLinuxCloud Organizational Unit Name (eg, section) []:Admin Common Name (eg, your name or your server's hostname) []:example.com Email Address []: Next verify the Subject Alternative Field in your CSR:

[root@controller certs]# openssl req -noout -text -in server.csr | grep -A 1 "Subject Alternative Name" X509v3 Subject Alternative Name: IP Address:10.10.10.13, IP Address:10.10.10.14, IP Address:10.10.10.17, DNS:centos8-2.example.com, DNS:centos8-3.example.com

Let me also cover some further modifications which can be done to a certificate

Add custom X.509 extensions to Certificate There are multiple x509 extensions which you can assign to your certificate.

This can be done by updating your openssl.cnf file or you can create a custom configuration file and use that to generate certificate. You may have noticed multiple extension fields in your openssl.cnf such as

v3_ca v3_req crl_ext proxy_cert_ext .. where each field contains separate set of x509 extensions. You can define the default extension under [req] section as shown below:

[ req ] default_bits = 2048 default_md = sha256 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca Alternatively you can use -extensions <extension_name> to use a specific extension while signing your certificate.

For example, here I have created an external configuration file with the list of x509 extensions I want to add to my certificate:

[root@controller certs]# cat custom_openssl.cnf authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth, clientAuth Now let me generate my certificate using this configuration file:

[root@controller certs]# openssl x509 -req -days 3650 -in server.csr -CA cacert.pem -CAkey ca.key -CAcreateserial -out server.crt -extfile custom_openssl.cnf Signature ok subject=C = IN, ST = KARNATAKA, L = BANGALORE, O = GoLinuxCloud, OU = Admin, CN = example Getting CA Private Key Verify the X.509 extensions inside the certificate:

[root@controller certs]# openssl x509 -noout -text -in server.crt | grep -A 10 "X509v3 extensions" X509v3 extensions: X509v3 Authority Key Identifier: keyid:AC:42:BE:30:0E:43:34:22:EC:1E:AA:5A:D1:9E:C6:C4:BA:72:0F:E0

        X509v3 Basic Constraints:
            CA:FALSE
        X509v3 Key Usage:
            Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
        X509v3 Extended Key Usage:
            TLS Web Server Authentication, TLS Web Client Authentication
Signature Algorithm: sha256WithRSAEncryption

Summary We have covered different areas related to generating CSR required to sign a certificate. Now you can use a CSR to sign either a RootCA certificate, server or client certificate. I have also explained the important points which one should consider when trying to create a CSR or else they may face TCP handshake failures or certificate signing related failures.

The Common Name is considered as the most important field of any Certificate and must be filled cautiously, especially when generating server or client certificate.

Let me know if you have any questions or feedbacks using the comments section.