In this tutorial I will share the step by step instructions to renew a SSL or TLS certificate using OpenSSL command. We will cover the following scenarios in this article:
- Renew SSL or TLS certificate after performing a Revocation
- Renew SSL or TLS certificate without performing Revocation
- Renew SSL or TLS certificate using existing SAN and X.509 extensions
Set up Lab Environment
First we will setup our lab environment so that you are familiar with the basic setup on which I will perform the certificate renewal. We will be performing the following steps as part of Lab Setup:
- Generate our own Root CA certificate
- Generate and sign server certificate using RootCA certificate
So, let's get started:
Generate RootCA certificate
Step-1: Create directory structure
We will maintain our RootCA database and other files inside /root/tls
:
[root@controller ~]# mkdir /root/tls/{certs,private,crl}
Create serial
and index.txt
file which we will use to track the signed certificates
[root@controller ~]# echo 01 > serial [root@controller ~]# touch index.txt
Copy the default openssl.cnf
to your custom path:
[root@controller ~]# cp /etc/pki/tls/openssl.cnf /root/tls/
Step-2: Sample openssl configuration file
Sample openssl.cnf
file for your reference:
[root@controller ~]# cat /root/tls/openssl.cnf # # OpenSSL example configuration file. # This is mostly being used for generation of certificate requests. # # Note that you can include other files from the main configuration # file using the .include directive. #.include filename # This definition stops the following lines choking if HOME isn't # defined. HOME = . # Extra OBJECT IDENTIFIER info: #oid_file = $ENV::HOME/.oid oid_section = new_oids # To use this configuration file with the "-extfile" option of the # "openssl x509" utility, name here the section containing the # X.509v3 extensions to use: # extensions = # (Alternatively, use a configuration file that has only # X.509v3 extensions in its main [= default] section.) # Load default TLS policy configuration openssl_conf = default_modules [ default_modules ] ssl_conf = ssl_module [ ssl_module ] system_default = crypto_policy [ crypto_policy ] .include /etc/crypto-policies/back-ends/opensslcnf.config [ 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/tls # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. #unique_subject = no # Set to 'no' to allow creation of # 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 crl = $dir/crl.pem # The current CRL private_key = $dir/private/cakey.pem# The private key x509_extensions = v3_ca # The extensions to add to the cert default_days = 3650 # how long to certify for default_crl_days= 30 # how long before next CRL default_md = sha512 # use SHA-256 by default preserve = no # keep passed DN ordering # A few difference way of specifying how similar the request should look # For type CA, the listed attributes must be the same, and the optional # and supplied fields are just that :-) policy = policy_match # For the CA policy [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional # For the 'anything' policy # At this point in time, you must list all acceptable 'object' # types. [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### [ req ] default_bits = 4096 default_md = sha512 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca # The extensions to add to the self signed cert string_mask = utf8only [ 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_ca ] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical,CA:true nsComment = "OpenSSL Generated Certificate" [ v3_req ] # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment
policy_match
policy for our RootCA. Under this policy we expect that any server or client certificate which is provided for signing purpose must match the countryName
, stateOrProvinceName
and organizationName
as mentioned under policy_match
rule. You can accordingly modify your configuration.
Step-3: Generate RootCA Private key
To generate a RootCA certificate, first we need a private key:
[root@controller tls]# openssl genrsa -out private/cakey.pem 4096
Sample Output:
Step-4: Generate RootCA certificate
Next we generate the RootCA certificate
[root@controller tls]# openssl req -new -x509 -days 3650 -config openssl.cnf -extensions v3_ca -key private/cakey.pem -out certs/cacert.pem
Sample Output:
List of files at this stage under /root/tls
:
[root@controller tls]# ls -l total 24 drwxr-xr-x 2 root root 4096 Aug 26 22:14 certs drwxr-xr-x 2 root root 4096 Aug 26 22:13 crl -rw-r--r-- 1 root root 0 Aug 26 22:10 index.txt -rw-r--r-- 1 root root 4505 Aug 26 22:13 openssl.cnf drwxr-xr-x 2 root root 4096 Aug 26 22:13 private -rw-r--r-- 1 root root 3 Aug 26 22:10 serial
Generate server certificate
Next we will generate the server certificate which will later forcefully expire:
Step-1: Create directory structure
We will store our server certificates inside /certs path:
[root@controller ~]# mkdir /certs
Step-2: Generate private key
The first step would be to generate a private key:
[root@controller certs]# openssl genrsa -out server.key.pem 4096
Sample Output:
Step-3: Generate CSR for server certificate
Next we use the existing private key to generate our CSR:
[root@controller certs]# openssl req -new -key server.key.pem -out server.csr
Sample Output:
Step-4: Generate server certificate
We intend to create a SAN certificate so we would need one configuration file:
[root@controller ~]# cat /certs/server_ext.conf
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 = 10.10.10.13
IP.2 = 10.10.10.14
IP.3 = 192.168.0.150
IP.4 = 192.168.43.104
DNS.1 = centos8-3.example.com
DNS.2 = controller.example.com
We will use this configuration file to generate the CSR for our SAN certificate.
Next we will generate and sign the server certificate using RootCA.
[root@controller certs]# openssl ca -config /root/tls/openssl.cnf -days 10 -notext -batch -in server.csr -out server.crt -extfile server_ext.conf
Sample Output:
Once we sign any certificate with RootCA then the RootCA database will be updated accordingly to keep a track of this. You can check the list of certificates signed by a RootCA referring the serial
and index.txt
file which we had created earlier:
[root@controller certs]# cat /root/tls/index.txt V 210905165814Z 01 unknown /C=IN/ST=Karnataka/O=GoLinuxCloud/OU=Admin/CN=example.com [root@controller certs]# cat /root/tls/serial 02
As you can see, the serial file number is automatically incremented to 02
which will be assigned to the next certificate which is signed by RootCA.
The 01
serial is assigned to CN=example.com
i.e. our server certificate which we just generated above.
Verify the server certificate along with RootCA:
[root@controller certs]# openssl verify -CAfile /root/tls/certs/cacert.pem -verbose server.crt
server.crt: OK
Verify the SAN field along with other extensions:
[root@controller certs]# openssl x509 -noout -text -in server.crt | grep -A 1 "Subject Alternative Name"
X509v3 Subject Alternative Name:
IP Address:10.10.10.13, IP Address:10.10.10.14, IP Address:192.168.0.150, IP Address:192.168.43.104, DNS:centos8-3.example.com, DNS:controller.example.com
Forcefully expire server certificate
Now to demonstrate a certificate renewal, we must make sure our certificate is expired right?
Actually there is no way you can forcefully expire a certificate, although there is a trick which I use to verify such scenarios in my lab setup.
The OpenSSL command will validate the expiry of a certificate based on your local server's date and timezone. So if we fast forward our server's date and time then we can trick OpenSSL to believe that the certificate has actually expired.
Let's test this, currently as you see my server certificate is expected to be expired on Sep 5 16:58:14 2021 GMT
.
[root@controller certs]# openssl x509 -noout -text -in server.crt | grep -i -A2 validity
Validity
Not Before: Aug 26 16:58:14 2021 GMT
Not After : Sep 5 16:58:14 2021 GMT
So I will change the sate of my Linux server to any date after September 5th
:
[root@controller certs]# date --set "10 Sep 2021 10:10:10"
Fri Sep 10 10:10:10 IST 2021
Now let us re-verify the certificate status:
[root@controller certs]# openssl verify -CAfile /root/tls/certs/cacert.pem -verbose server.crt
C = IN, ST = Karnataka, O = GoLinuxCloud, OU = Admin, CN = example.com
error 10 at 0 depth lookup: certificate has expired
error server.crt: verification failed
Voila! The trick worked!
So now our certificate is assumed to be expired and we can run our tests to renew this certificate using openssl.
Renew SSL or TLS certificate using OpenSSL
Now one more thing you should understand that there is nothing like renewing a certificate, i.e. you cannot extend the expiry of an existing certificate.
What we do is basically, use the existing private key and then either
- You generate a new CSR and a new certificate using the same private key
- Or you use the existing CSR along with private key to generate a new certificate
The advantage of using an existing CSR is that all of your x.509 extensions will be retained in the new server certificate.
Scenario-1: Renew a certificate after performing revocation
Now if you have followed our Lab Setup, then you must have observed that we are maintaining a database of all the certificates which are signed by our CA certificate.
In such case if you re-attempt to generate a new certificate using existing CSR and private key then openssl will fail to generate the certificate with following ERROR:
[root@controller certs]# openssl ca -config /root/tls/openssl.cnf -days 10 -notext -batch -in server.csr -out server-renewed.crt
Using configuration from /root/tls/openssl.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName :PRINTABLE:'IN'
stateOrProvinceName :ASN.1 12:'Karnataka'
localityName :ASN.1 12:'BANGALORE'
organizationName :ASN.1 12:'GoLinuxCloud'
organizationalUnitName:ASN.1 12:'Admin'
commonName :ASN.1 12:'example.com'
ERROR:There is already a certificate for /C=IN/ST=Karnataka/O=GoLinuxCloud/OU=Admin/CN=example.com
The matching entry has the following details
Type :Valid
Expires on :210905165814Z
Serial Number :01
File name :unknown
Subject Name :/C=IN/ST=Karnataka/O=GoLinuxCloud/OU=Admin/CN=example.com
As you can see, we get an error that There is already a certificate for /C=IN/ST=Karnataka/O=GoLinuxCloud/OU=Admin/CN=example.com
. So, RootCA will not sign a certificate with the same Common Name unless we revoke the existing certificate.
Step-1: Revoke the existing server certificate
So let's go ahead and revoke this certificate so we can renew one:
[root@controller certs]# openssl ca -revoke server.crt -config /root/tls/openssl.cnf
Using configuration from /root/tls/openssl.cnf
Revoking Certificate 01.
Data Base Updated
For more information you can read How to revoke the certificate and generate a CRL with openssl
Step-2: Generate a Certificate Revocation List (CRL)
We will need to create a Certificate Revocation List file which will maintain the list of all such certificates which are revoked. Similar to the serial file which we had created to track the list of signed certificates earlier, we will have to create another file crlnumber
to track the serial number of revoked certificates:
[root@controller certs]# cd /root/tls/
We can assign any serial number into this file:
[root@controller tls]# echo 1000 > crlnumber
Next generate a CRL list:
[root@controller tls]# openssl ca -gencrl -out crl/example.crl -config openssl.cnf
Using configuration from /root/tls/openssl.cnf
Verify the CRL file:
[root@controller tls]# openssl crl -in crl/example.crl -text -noout
Certificate Revocation List (CRL):
Version 2 (0x1)
Signature Algorithm: sha512WithRSAEncryption
Issuer: C = IN, ST = Karnataka, L = BANGALORE, O = GoLinuxCloud, OU = Admin, CN = RootCA
Last Update: Sep 10 04:48:05 2021 GMT
Next Update: Oct 10 04:48:05 2021 GMT
CRL extensions:
X509v3 CRL Number:
4096
Revoked Certificates:
Serial Number: 01
Revocation Date: Sep 10 04:45:11 2021 GMT
Signature Algorithm: sha512WithRSAEncryption
68:1a:c6:d8:27:f6:ec:72:f9:c6:0b:9d:d8:7f:65:bb:3b:91:
b0:60:ca:ba:bd:95:3a:2f:b6:6b:a7:09:5a:15:d0:ad:9d:68:
...
As you can see under Revoked Certificates, the provided Serial Number is 01 which was assigned to our server certificate with CN=example.com
You can confirm the same using index.txt
file:
[root@controller certs]# cat /root/tls/index.txt R 210905165814Z 210910044511Z 01 unknown /C=IN/ST=Karnataka/O=GoLinuxCloud/OU=Admin/CN=example.com
Notice the first character in the index.txt
file for 01
serial number, here R represents Revoked
Step-3: Renew server certificate
Now we can go ahead and renew our server certificate using the existing CSR and private key file:
[root@controller certs]# openssl ca -config /root/tls/openssl.cnf -days 10 -notext -batch -in server.csr -out server-renewed.crt
Using configuration from /root/tls/openssl.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName :PRINTABLE:'IN'
stateOrProvinceName :ASN.1 12:'Karnataka'
localityName :ASN.1 12:'BANGALORE'
organizationName :ASN.1 12:'GoLinuxCloud'
organizationalUnitName:ASN.1 12:'Admin'
commonName :ASN.1 12:'example.com'
Certificate is to be certified until Sep 20 04:48:51 2021 GMT (10 days)
As expected, we don't get any error anymore. Verify the index.txt file again as we should have a new entry of the newly signed certificate:
[root@controller certs]# cat /root/tls/index.txt
R 210905165814Z 210910044511Z 01 unknown /C=IN/ST=Karnataka/O=GoLinuxCloud/OU=Admin/CN=example.com
V 210920044851Z 02 unknown /C=IN/ST=Karnataka/O=GoLinuxCloud/OU=Admin/CN=example.com
Step-4: Verify renewed server certificate
Verify the renewed certificate against the RootCA certificate:
[root@controller certs]# openssl verify -CAfile /root/tls/certs/cacert.pem -verbose server-renewed.crt
server-renewed.crt: OK
Verify the SAN field and other x509 extensions:
[root@controller certs]# openssl x509 -noout -text -in server-renewed.crt | grep -A 1 "Subject Alternative Name"
X509v3 Subject Alternative Name:
IP Address:10.10.10.13, IP Address:10.10.10.14, IP Address:192.168.0.150, IP Address:192.168.43.104, DNS:centos8-3.example.com, DNS:controller.example.com
So all our SAN field are intact, you can similarly grep for other X509v3 extension fields in the server certificate.
Scenario-2: Renew certificate with a new CSR
It is possible that you may have lost the Certificate Signing request using which your original certificate was generated. Although it is important that you have the same private key which was used to generate the server certificate.
Step-1: Export CSR from the Server certificate
Since we do not have a Certificate Signing request, we will export the CSR from the expired server certificate:
[root@controller certs]# openssl x509 -x509toreq -in server.crt -signkey server.key.pem -out server.csr Getting request Private Key Generating certificate request
This step will use the existing private key and expired server certificate to generate a CSR. You can check the content of the CSR using:
[root@controller certs]# openssl req -noout -text -in server.csr
Next we will quickly revoke our certificate, to generate a new one:
[root@controller certs]# openssl ca -revoke server-renewed.crt -config /root/tls/openssl.cnf
Using configuration from /root/tls/openssl.cnf
Revoking Certificate 03.
Data Base Updated
Step-2: Renew server certificate
Now we can renew our server certificate and create a new one. But since we would like to retain our SAN fields and other X.509 extensions, we will use a similar external configuration file:
[root@controller certs]# openssl ca -config /root/tls/openssl.cnf -days 10 -notext -batch -in server.csr -out server-renewed-1.crt -extfile server_ext.conf
Using configuration from /root/tls/openssl.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName :PRINTABLE:'IN'
stateOrProvinceName :ASN.1 12:'Karnataka'
localityName :ASN.1 12:'BANGALORE'
organizationName :ASN.1 12:'GoLinuxCloud'
organizationalUnitName:ASN.1 12:'Admin'
commonName :ASN.1 12:'example.com'
Certificate is to be certified until Sep 20 05:31:33 2021 GMT (10 days)
Write out database with 1 new entries
Data Base Updated
Step-3: Verify renewed server certificate
Verify the server certificate against the RootCA:
[root@controller certs]# openssl verify -CAfile /root/tls/certs/cacert.pem -verbose server-renewed-1.crt
server-renewed-1.crt: OK
Verify the SAN field in the newly generated server certificate:
[root@controller certs]# openssl x509 -noout -text -in server-renewed-1.crt | grep -A 1 "Subject Alternative Name"
X509v3 Subject Alternative Name:
IP Address:10.10.10.13, IP Address:10.10.10.14, IP Address:192.168.0.150, IP Address:192.168.43.104, DNS:centos8-3.example.com, DNS:controller.example.com
Scenario-3: Renew server certificate without revocation
This is only possible if your root CA is not maintaining any database of the certificates which were signed. Since in our Lab Setup we were maintaining index.txt file with the list of signed certificates, we were bound to reoke a certificate before renewing one with the same CN.
Step-1: Setup Lab Environment
So let's generate a RootCA using a different command. I will use the existing root CA private key:
[root@controller certs]# openssl req -new -x509 -days 3650 -key cakey.pem -out ca.cert.pem
Sample Output:
Let me generate a fresh server certificate:
[root@controller certs]# openssl x509 -req -days 365 -in server.csr -CA ca.cert.pem -CAkey cakey.pem -CAcreateserial -out server-1.crt -extfile server_ext.conf
Signature ok
subject=C = IN, ST = Karnataka, L = BANGALORE, O = GoLinuxCloud, OU = Admin, CN = example.com
Getting CA Private Key
Verify the certificate status
[root@controller certs]# openssl verify -CAfile ca.cert.pem -verbose server-1.crt
server-1.crt: OK
Let us change the date of my Linux node to expire the certificate manually:
[root@controller certs]# date --set "11 Sep 2023 10:10:10" Mon Sep 11 10:10:10 IST 2023 [root@controller certs]# openssl verify -CAfile ca.cert.pem -verbose server-1.crt C = IN, ST = Karnataka, L = BANGALORE, O = GoLinuxCloud, OU = Admin, CN = example.com error 10 at 0 depth lookup: certificate has expired error server-1.crt: verification failed
Next we can continue to renew our certificate and this time we don't have to perform any revocation as our CA certificate is not maintaining any database of all the signed certificates.
Step-2: Renew server certificate
We will use our existing CSR and private key file to renewal the server certificate:
[root@controller certs]# openssl x509 -req -days 365 -in server.csr -CA ca.cert.pem -CAkey cakey.pem -CAcreateserial -out server-2.crt -extfile server_ext.conf
Signature ok
subject=C = IN, ST = Karnataka, L = BANGALORE, O = GoLinuxCloud, OU = Admin, CN = example.com
Getting CA Private Key
So we have successfully renewed and generated a fresh server certificate using our existing CSR and private key:
Step-3: Verify renewed certificate
Verify the SAN fields:
[root@controller certs]# openssl x509 -noout -text -in server-2.crt Certificate: Data: Version: 3 (0x2) Serial Number: 37:3e:d1:d3:96:3c:83:2e:dc:52:62:fc:36:12:f7:99:eb:51:26:fb Signature Algorithm: sha256WithRSAEncryption Issuer: C = IN, ST = Karnataka, L = Bangalore, O = GoLinuxCloud, OU = Admin, CN = rootca.com Validity Not Before: Sep 11 04:42:27 2023 GMT Not After : Sep 10 04:42:27 2024 GMT Subject: C = IN, ST = Karnataka, L = BANGALORE, O = GoLinuxCloud, OU = Admin, CN = example.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (4096 bit) Modulus: 00:ad:e4:e3:17:b2:2a:54:37:a3:3e:00:60:a4:f9: c5:6b:9e:13:e9:29:53:3c:f2:b1:24:e2:15:d0:6b: 0b:67:f2:89:8e:0c:e8:88:29:58:fa:18:fb:1d:20: ... 72:28:37:87:2b:6d:48:a5:0d:8c:f2:0f:a6:0f:11: 1c:9f:3e:bc:34:f2:d2:3c:29:c7:94:2d:94:ab:69: cf:b9:d7 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Cert Type: SSL Server Netscape Comment: OpenSSL Generated Server Certificate X509v3 Subject Key Identifier: 8A:7C:7E:50:2D:E5:3C:53:48:68:8A:EA:34:43:A1:05:96:79:BC:39 X509v3 Authority Key Identifier: keyid:19:55:26:4A:F8:E8:08:92:36:9F:7D:AD:EC:1B:4C:85:3E:C6:78:D5 DirName:/C=IN/ST=Karnataka/L=Bangalore/O=GoLinuxCloud/OU=Admin/CN=rootca.com serial:0B:B7:07:EE:67:09:74:62:86:4C:26:08:0A:55:16:DB:57:43:53:08 X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication X509v3 Subject Alternative Name: IP Address:10.10.10.13, IP Address:10.10.10.14, IP Address:192.168.0.150, IP Address:192.168.43.104, DNS:centos8-3.example.com, DNS:controller.example.com Signature Algorithm: sha256WithRSAEncryption 61:a2:46:f1:a4:f5:5f:b4:cd:75:a9:b3:59:c1:7f:3a:b5:d1: 05:56:c1:8f:85:c5:43:9f:3b:bf:da:7d:bf:5e:da:6a:9f:d7: 56:d5:dd:29:2e:63:27:d4:26:6c:9d:53:6b:99:39:90:e8:79: a3:9b:23:84:e9:26:72:c6:28:bf:59:80:55:75:c1:cb:b8:b3: 33:09:32:39:19:3b:a1:e6:f3:68:42:7b:83:d1:a4:59:5d:1c: ...
Summary
In this article we learned on how to renew SSL server or client certificate using OpenSSL. We covered different scenarios for renewal so that you can decide to either retain your existing SAN fields and X.509 extensions or you may decide to discard them during the renewal.
We do term this whole process as renewal but actually we are just generating a new certificate using existing private key and CSR. One should not get confused that we are extending the expiry of the certificate.
If you have any questions or concerns, feel free to ask them using the comments box.