Table of Contents
Servers now a days have multiple common names, commonly used websites like youtube, have youtube.com, yt.be, youtu.be as it aliases. So it is not economic to have certificates for individual domain alias names. For tackling this multiple common name problem, SAN was introduced. SAN stands for subject Alternative Name. Article explaining MTLS clearly showed the need of SAN fields. Below error was seen when the requested domain name didn't matched the server's common name.
curl: (51) Unable to communicate securely with peer: requested domain name does not match the server’s certificate.
In this article, we see the creation and application of SAN CSR and Certs with openssl
Generate CSR with SAN using openssl
Generate private key
We need to generate private key, which we will using for creating the CSR
[root@3-vcp san_test]# openssl genrsa -out SAN_Privatekey.key 2048 Generating RSA private key, 2048 bit long modulus .......................................................+++ .......................................................+++ e is 65537 (0x10001)
Create conf file for CSR
We will create a conf file for the CSR, when conf file is used. User will not be prompted for entering the details. This is commonly used for automating and bulk creating CSRs. In the below code snippet, it can be seen that, we are providing Name of the server and the IP address as the alternate name
[root@3-vcp san_test]# cat SAN_conf.conf [ req ] default_bits = 2048 distinguished_name = req_distinguished_name req_extensions = req_ext prompt = no [ req_distinguished_name ] countryName = UG stateOrProvinceName = Uganda localityName = Naira organizationName = LinuxDataHub-Uganda commonName = LDH-syndicate [ req_ext ] subjectAltName = @alt_names [alt_names] DNS.1 = LDH-review-server IP.1 = 10.55.7.41
Generate CSR with SAN
We can use the below command to create CSR.
openssl req -out csr_with_san.csr -new -key SAN_Privatekey.key -config SAN_conf.conf
Verify the CSR for SAN fields
Using the openssl req -text command we can view the content of the CSR, to verify the SAN fields
[root@3-vcp san_test]# openssl req -noout -text -in csr_with_san.csr
Certificate Request:
Data:
Version: 0 (0x0)
Subject: C=UG, ST=Uganda, L=Naira, O=LinuxDataHub-Uganda, CN=LDH-syndicate
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
<trimmed>
Exponent: 65537 (0x10001)
Attributes:
Requested Extensions:
X509v3 Subject Alternative Name:
DNS:LDH-review-server, IP Address:10.55.7.41
Generate Certificate with SAN fields
We will generating a certificate from the CSR. For this we will have to sign the CSR with a CA. We already have a CA which we have created and we will be reusing that CA for signing our CSR.
[root@3-vcp san_test]# openssl x509 -req -in csr_with_san.csr -CA ../first_ca/myCA_firstCert.pem -CAkey ../first_ca/myCA_first.key -CAcreateserial -out SAN_First_SignedCertificate.crt -days 365 -sha256 -extensions req_ext -extfile SAN_conf.conf
Signature ok
subject=/C=UG/ST=Uganda/L=Naira/O=LinuxDataHub-Uganda/CN=LDH-syndicate
Getting CA Private Key
Verify the SAN fields in the Certificate
Below snippet confirms, that our certificate is having SAN fields
[root@3-vcp san_test]# openssl x509 -text -noout -in SAN_First_SignedCertificate.crt Certificate: Data: Version: 3 (0x2) Serial Number: ef:32:a5:ae:26:68:84:0f 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 7 09:25:29 2022 GMT Not After : Aug 7 09:25:29 2023 GMT Subject: C=UG, ST=Uganda, L=Naira, O=LinuxDataHub-Uganda, CN=LDH-syndicate Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) X509v3 extensions: X509v3 Subject Alternative Name: DNS:LDH-review-server, IP Address:10.55.7.41
Connection Verification using SAN certificate
We will be running a sample server with the created SAN certificate and we will try to connect with a client, with both san fields.
Running a Sample server with generated SAN cert
The sample server is run using the generated SAN cert. Sample code for running the server can be seen here. I will be providing a CA cert for the purpose of MTLS verification.
More on the MTLS openssl verification can be found in details on article where MTLS is simulated
[root@3-vcp LDH]# python3 server.py san_test/SAN_First_SignedCertificate.crt san_test/SAN_Privatekey.key first_ca/myCA_firstCert.pem LDH-review-server File Server started at https://LDH-review-server:443
Client side verification
We will use curl to connect to the server, we will we using client certificate, key and CA for doing the same.
We will be simulating both successful and failure scenarios.
Scenario #1
We will be connecting with the DNS entry we made (LDH-review-server). As seen below the connection is successful
[root@test-re Client]# curl https://LDH-review-server: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="san_mtls.pcap">san_mtls.pcap</a></li>
<li><a href="san_test/">san_test/</a></li>
<li><a href="second_ca/">second_ca/</a></li>
<li><a href="server.py">server.py</a></li>
<li><a href="server1.py">server1.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>
Scenario #2
We will be connecting with the IP address entry we made (10.55.7.41). As seen below the connection is successful
[root@test-re Client]# curl https://10.55.7.41: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="san_test/">san_test/</a></li>
<li><a href="second_ca/">second_ca/</a></li>
<li><a href="server.py">server.py</a></li>
<li><a href="server1.py">server1.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>
Scenario #3
We will be trying to connect with a domain which is not part of the server certificate but is resolving to the same server's ip address. It can be seen that, we are not able to communicate securely to the server, as the domain is not matching any of the san fields in the server's certificate
#Ping shows the same ipaddress which we used for Scenario #2 [root@test-re Client]# ping LDH-syndicate PING LDH-syndicate (10.55.7.41) 56(84) bytes of data. 64 bytes from LDH-syndicate (10.55.7.41): icmp_seq=1 ttl=64 time=0.917 ms [root@test-re Client]# curl https://LDH-syndicate:443 --cert ./Client_First_SignedCertificate.crt --key ./client_CSRPrivate.key --cacert ../first_ca/myCA_firstCert.pem curl: (51) Unable to communicate securely with peer: requested domain name does not match the server's certificate.
PCAP with SAN fields
Below pic shows the pcap analysed with wireshark. It can be clearly seen, the server's certificate showed the SAN fields to the client