Generate CSR with SAN using openssl

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

GENERATE CSR WITH SAN USING OPENSSL

Read More

Search on LinuxDataHub

Leave a Comment