PKI OpenSSL (pt 3)

Client and Server Certificates

Now that we have our IntermediateCA properly configured, we can start using it to issue certificates.
Notes:

  • You should use no less than half the bits used on the CA for keys
  • The server’s CN should be its FQDN
  • A server certificate needs to have the same FQDN repeated on the SAN information
  • The client certificate’s CN can be anything, it’s not important.
  • Certificates must be signed by the IntermediateCA
  • Certificates usually last a year (365 days) and the maximum, since July 2019, is 824 days (+1)
  • OpenSSL command for server certificate: -extensions server_cert
  • OpenSSL command for client certificate: -extensions usr_cert
  • Only if copy_extensions = copy or = all is on the [ca_default] section of intermediate/openssl.conf can the CSR’s SAN information be transferred to the signed certificate; otherwise, the SAN information has to be reinserted when using the command x509 to sign the CSR, so that it reaches the signed certificate.
  • 4 servers will be used:
    • offline: has the RootCA
    • server: has the CRLDistribution of the RootCA, the IntermediateCA and its certificates, and the IntermediateCA’s OCSP Responder
    • www: will have an HTML server certified by the IntermediateCA
    • client: will access the site on www

Example of server certificate: the PKI server’s own certificate

Start by creating a key; in this case you should omit the -aes256 or the -passout option to avoid creating a password.
If there is a password, it will be necessary to introduce it whenever the webserver is started…

1
2
inter="/root/ca/inter-ca"
openssl genrsa -out $inter/private/server.tjs.lan.key.pem 4096

Generating RSA private key, 4096 bit long modulus (2 primes)
...............................................................................................................++++
........................................................++++
e is 65537 (0x010001)

1
chmod 400 $inter/private/server.tjs.lan.key.pem

Create SAN information

It’s especially useful when the FQDN is fake or not available (e.g, there are no DNS records and you have to certify by IP address), but it’s become mandatory because now the certificates for Web Servers must to have the FQDN in a SAN entry, and having it only in the CN is no longer enough…
To do so, we have to create an extra .CNF file, containing only this information (which will be different for each server)

1
2
mkdir $inter/san
cat > $inter/san/server.tjs.lan-san.cnf
[req]
distinguished_name = req_distinguished_name
req_extensions = req_ext
prompt = no

[req_distinguished_name]
C   = PT
ST  = Lisboa
L   = Lisboa
O   = Tiago Joao Silva
OU  = TJS
CN  = server.tjs.lan
[req_ext]
subjectAltName = @alt_names

[alt_names]
IP.1 = 192.168.1.70
DNS.1 = server.tjs.lan 
DNS.2 = dns.tjs.lan
DNS.3 = chat.tjs.lan
DNS.4 = mail.tjs.lan

Create the CSR

1
2
3
4
openssl req -config $inter/openssl.cnf \
    -key $inter/private/server.tjs.lan.key.pem \
	-config $inter/san/server.tjs.lan-san.cnf \
    -new -sha512 -out $inter/csr/server.tjs.lan.csr.pem

Confirm that the CSR has the SAN info:

1
2
openssl req -noout -text -in $inter/csr/server.tjs.lan.csr.pem | \
grep -A 1 "Subject Alternative Name"

    X509v3 Subject Alternative Name:
        IP Address:192.168.1.70, DNS:server.tjs.lan, DNS:dns.tjs.lan, DNS:chat.tjs.lan, DNS:mail.tjs.lan

Sign the CSR

Since July 2019 the maximum duration of a server certificate is 824 days (+today) and the most recent browsers reject certificates with more duration…

In case there’s no copy\_extensions = copy or = all in inter-ca/openssl.cnf, you will need to reinsert SAN the information with the options -extensions req_ext and -extfile aaa-san.cnf

1
2
3
4
5
6
7
openssl ca -config $inter/openssl.cnf \
    -extensions server_cert -days 823 -notext -md sha512 \
	-extensions req_ext \
	-extfile $inter/san/server.tjs.lan-san.cnf \
	-passin file:$inter/private/inter-ca.key.pass \
    -in $inter/csr/server.tjs.lan.csr.pem \
    -out $inter/certs/server.tjs.lan.cert.pem

Otherwise, if copy\_extensions = copy or = all is in inter-ca/openssl.cnf, it’s a lot easier:

1
2
3
4
5
openssl ca -config $inter/openssl.cnf \
    -extensions server_cert -days 823 -notext -md sha512 \
	-passin file:$inter/private/inter-ca.key.pass \
    -in $inter/csr/server.tjs.lan.csr.pem \
    -out $inter/certs/server.tjs.lan.cert.pem

Using configuration from /root/ca/inter-ca/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 4100 (0x1004)
        Validity
            Not Before: Aug 22 23:54:31 2021 GMT
            Not After : Nov 23 23:54:31 2023 GMT
        Subject:
            countryName               = PT
            stateOrProvinceName       = Lisboa
            localityName              = Lisboa
            organizationName          = Tiago Joao Silva
            organizationalUnitName    = TJS
            commonName                = server.tjs.lan
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Cert Type:
                SSL Server
            Netscape Comment:
                OpenSSL Generated Server Certificate
            X509v3 Subject Key Identifier:
                94:00:E3:58:4F:BA:20:85:F7:9C:DC:47:3C:68:A1:46:8B:78:10:AF
            X509v3 Authority Key Identifier:
                keyid:E3:75:25:D7:74:08:0B:23:F0:AF:E4:EC:D0:D4:52:CB:5A:3A:10:2E
                DirName:/C=PT/ST=Lisboa/L=Lisboa/O=TiagoJoaoSilva/OU=TJS/CN=ca-tjs-1/emailAddress=bofh@tjs.lan
                serial:10:03

            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            Authority Information Access:
                OCSP - URI:http://ocsp.tjs.lan:7888

Certificate is to be certified until Nov 23 23:54:31 2023 GMT (823 days)
Sign the certificate? [y/n]:Y

1 out of 1 certificate requests certified, commit? [y/n]Y
Write out database with 1 new entries
Data Base Updated

1
chmod 444 $inter/certs/server.tjs.lan.cert.pem

Verify the ChainOfTrust with the CA-CHAIN certificate

1
2
openssl verify -CAfile $inter/certs/inter-ca_chain.cert.pem \
	$inter/certs/server.tjs.lan.cert.pem 

/root/ca/inter-ca/certs/server.tjs.lan.cert.pem: OK

Create a Chain/Bundle certificate for the webserver

Splice the Server certificate and the CA-CHAIN certificate:

1
2
cat $inter/certs/{server.tjs.lan.cert.pem,inter-ca_chain.cert.pem} > \
	$inter/certs/server.tjs.lan.cert_chain.pem

Testing the CA

Set up a webserver

We need to create a new certificate for the webserver. Its IP will be 192.168.1.77 and its hostname www

1
2
3
4
inter="/root/ca/inter-ca"
openssl genrsa -out $inter/private/www.tjs.lan.key.pem 4096
chmod 400 $inter/private/www.tjs.lan.key.pem
cat > $inter/san/www.tjs.lan-san.cnf
[req]
distinguished_name = req_distinguished_name
req_extensions = req_ext
prompt = no

[req_distinguished_name]
C   = PT
ST  = Lisboa
L   = Lisboa
O   = Tiago Joao Silva
OU  = TJS
CN  = www.tjs.lan
[req_ext]
subjectAltName = @alt_names

[alt_names]
IP.1 = 192.168.1.77
DNS.1 = www.tjs.lan 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
openssl req -config $inter/openssl.cnf \
    -key $inter/private/www.tjs.lan.key.pem \
	-config $inter/san/www.tjs.lan-san.cnf \
    -new -sha512 -out $inter/csr/www.tjs.lan.csr.pem
openssl ca -config $inter/openssl.cnf \
    -extensions server_cert -days 823 -notext -md sha512 \
	-passin file:$inter/private/inter-ca.key.pass \
    -in $inter/csr/www.tjs.lan.csr.pem \
    -out $inter/certs/www.tjs.lan.cert.pem
chmod 444 $inter/certs/www.tjs.lan.cert.pem
cat $inter/certs/{www.tjs.lan.cert.pem,inter-ca_chain.cert.pem} > \
	$inter/certs/www.tjs.lan.cert_chain.pem
chmod 444 $inter/certs/www.tjs.lan.cert_chain.pem

To test, install a new Debian 11 system without GUI.
Edit the configuration of the desired interface (enp0s3 is the primary interface on VirtualBox; on VMware it’s ens33)

1
vim /etc/network/interfaces
iface enp0s3 inet static
address 192.168.1.77/24
gateway 192.168.1.1
1
hostnamectl set-hostname www.tjs.lan

Edit /etc/hosts; in the second line, change to:

127.0.1.1	www.tjs.lan		www

Verify:

1
for opt in {s,d,f}; do hostname -$opt ; done


www
tjs.lan
www.tjs.lan


Install the NGINX server

1
apt install nginx -y

Check if the default page is being served:

1
wget localhost:80 -O-
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br>/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Bring the webserver’s Chain/Bundle certificate to the webserver e put it on a place that is not affected by system updates, like a local-certs folder in /etc/ssl/

1
2
3
mkdir /etc/ssl/local-certs
cp www.tjs.lan.cert_chain.pem /etc/ssl/local-certs/
chmod 444 /etc/ssl/local-certs/www.tjs.lan.cert_chain.pem

Also bring the private key associated with the webserver’s certificate and put it in /etc/ssl/private/

1
2
cp www.tjs.lan.key.pem /etc/ssl/private/
chmod 400 /etc/ssl/private/www.tjs.lan.key.pem

Change the NGINX configuration to automatically redirect port 80 (HTTP) requests to port 443 (https), and point to the the webserver’s certificate and key

1
2
cp /etc/nginx/sites-available/default{,.orig}
vim /etc/nginx/sites-available/default

From line 21:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
server {
	# redirect all HTTP to HTTPS
    listen 		80;
    listen 		[::]:80;
    server_name	www.tjs.lan;
    return 301 	https://www.tjs.lan$request_uri;
}

server {
    listen              	443 ssl;
    listen              	[::]:443 ssl;
    server_name         	www.tjs.lan;
    ssl_certificate     	/etc/ssl/local-certs/www.tjs.lan.cert_chain.pem;
    ssl_certificate_key 	/etc/ssl/private/www.tjs.lan.key.pem;

Note that we haven’t closed the braces, because the default configuration continues for a few more lines and the braces close below this point.

1
2
systemctl restart nginx
wget localhost:80 -O-


--2021-08-24 21:45:45--  http://localhost/
Resolving localhost (localhost)... ::1, 127.0.0.1
Connecting to localhost (localhost)|::1|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://www.tjs.lan/ [following]
--2021-08-24 21:45:45--  https://www.tjs.lan/
Resolving www.tjs.lan (www.tjs.lan)... 127.0.1.1
Connecting to www.tjs.lan (www.tjs.lan)|127.0.1.1|:443... connected.
ERROR: The certificate of ‘www.tjs.lan’ is not trusted.
ERROR: The certificate of ‘www.tjs.lan’ doesn't have a known issuer.


The redirection is happening, but the local wget to www.tjs.lan doesn’t trust the server certificate because it’s doesn’t know the RootCA’s certificate.

Open ports 80 and 443 on the firewall

1
2
ufw allow 80
ufw allow 443

Test the WebServer

Set up a GUI client (for example, a Debian 11 with MATE).

Name resolution

As we don’t have a DNS server with the tjs.lan zone nor records for all these systems, add the following to the /etc/hosts on the three systems (server, www and client)

# printf "\
#tjs.lan\n\
192.168.1.70	server.tjs.lan      server\n\
192.168.1.70	ocsp.tjs.lan	    ocsp\n\
192.168.1.77	www.tjs.lan         www \n" \
    >> /etc/hosts

Install the root certificate on the end-user

Import the RootCA’s certificate to the current user’s home.
Open Firefox, Preferences, Privacy and Security, View Certificates…
Or (about:preferences#privacy)


Import…

Pick the RootCA’s certificate, Open

Trust this CA to identify websites
Trust this CA to identify email users (optional)
OK

Clicking on View, the certificate’s details are shown

The Root Authority has been imported, OK

Test the site

Open http://www.tjs.lan (it may be necessary to tell Firefox that we really want to go to that address), and we have an SSL connection protected with our certificate:

Configure OCSP Stapling

Source: https://www.digitalocean.com/community/tutorials/how-to-configure-ocsp-stapling-on-apache-and-nginx
Bring the RootCA’s certificate to the webserver:

1
cp ca.cert.pem /etc/ssl/local-certs/

Add the following lines to NGINX’s active site (we were using /etc/nginx/sites-available/default), below ssl_certificate_key:

1
2
3
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/local-certs/ca.cert.pem;

Testing

From the client, test the OCSP stapling process with the following command, not forgetting to indicate the CA certificate (which we left in the user’s homefolder when we imported it into Firefox); if you deleted it, you need to bring it back to the client so that it can be used now:

1
echo QUIT | sudo openssl s_client -connect www.tjs.lan:443 -CAfile ~/ca.cert.pem -status

CONNECTED(00000003)
depth=2 C = PT, ST = Lisboa, L = Lisboa, O = TiagoJoaoSilva, OU = TJS, CN = ca-tjs-1, emailAddress = bofh@tjs.lan
verify return:1
depth=1 C = PT, ST = Lisboa, O = TiagoJoaoSilva, OU = TJS, CN = inter-ca-tjs-2, emailAddress = bofh@tjs.lan
verify return:1
depth=0 C = PT, ST = Lisboa, L = Lisboa, O = Tiago Joao Silva, OU = TJS, CN = www.tjs.lan
verify return:1
OCSP response: 
======================================
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response
    Version: 1 (0x0)
    Responder Id: C = PT, ST = Lisboa, L = Lisboa, O = Tiago Joao Silva, OU = TJS, CN = ocsp.tjs.lan
    Produced At: Aug 24 22:18:56 2021 GMT
    Responses:
    Certificate ID:
      Hash Algorithm: sha1
      Issuer Name Hash: AE59C598DC56A005EA5239AE471093B7924F5018
      Issuer Key Hash: E37525D774080B23F0AFE4ECD0D452CB5A3A102E
      Serial Number: 1007
    Cert Status: good
    This Update: Aug 24 22:18:56 2021 GMT
[…]
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 4099 (0x1003)
        Signature Algorithm: sha512WithRSAEncryption
        Issuer: C=PT, ST=Lisboa, O=TiagoJoaoSilva, OU=TJS, CN=inter-ca-tjs-2/emailAddress=bofh@tjs.lan
        Validity
            Not Before: Aug 22 22:53:56 2021 GMT
            Not After : Nov 23 22:53:56 2023 GMT
        Subject: C=PT, ST=Lisboa, L=Lisboa, O=Tiago Joao Silva, OU=TJS, CN=ocsp.tjs.lan
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (4096 bit)
[…]
---
Certificate chain
 0 s:C = PT, ST = Lisboa, L = Lisboa, O = Tiago Joao Silva, OU = TJS, CN = www.tjs.lan
   i:C = PT, ST = Lisboa, O = TiagoJoaoSilva, OU = TJS, CN = inter-ca-tjs-2, emailAddress = bofh@tjs.lan
 1 s:C = PT, ST = Lisboa, O = TiagoJoaoSilva, OU = TJS, CN = inter-ca-tjs-2, emailAddress = bofh@tjs.lan
   i:C = PT, ST = Lisboa, L = Lisboa, O = TiagoJoaoSilva, OU = TJS, CN = ca-tjs-1, emailAddress = bofh@tjs.lan
 2 s:C = PT, ST = Lisboa, L = Lisboa, O = TiagoJoaoSilva, OU = TJS, CN = ca-tjs-1, emailAddress = bofh@tjs.lan
   i:C = PT, ST = Lisboa, L = Lisboa, O = TiagoJoaoSilva, OU = TJS, CN = ca-tjs-1, emailAddress = bofh@tjs.lan
---
Server certificate
-----BEGIN CERTIFICATE-----
[…]
subject=C = PT, ST = Lisboa, L = Lisboa, O = Tiago Joao Silva, OU = TJS, CN = www.tjs.lan

issuer=C = PT, ST = Lisboa, O = TiagoJoaoSilva, OU = TJS, CN = inter-ca-tjs-2, emailAddress = bofh@tjs.lan

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 11091 bytes and written 392 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 4096 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: 18DD0A571D36D69EC35D46FBC8F28711DDEED91000F1F595897B061640DD86E9
    Session-ID-ctx: 
    Resumption PSK: 2B54CFDFF0170EDC5F77AE462A18AAA4DC7CC3CED7B21E2F6B97454A0AB65725AE63DDA084379C793DADF63983606283
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 28 c6 3d 71 b3 5c 60 1f-a8 7d 5f f7 26 3c cd f5   (.=q.\`..}_.&<..
    0010 - 07 ca 09 c9 4f 92 45 7a-28 a0 ff bc 1e 5a d2 19   ....O.Ez(....Z..
    0020 - 1f 19 d4 47 06 20 45 5e-f5 68 90 74 c1 66 e8 5b   ...G. E^.h.t.f.[
    0030 - 9d aa eb 47 36 2e 6f e0-4b d3 05 a3 5c cf 29 c0   ...G6.o.K...\.).
    0040 - 15 b6 f2 07 1b d5 49 df-33 f9 3b 9d 86 5a 43 dc   ......I.3.;..ZC.
    0050 - 5e 82 3b 8b 67 0a 57 99-ac 1b cc e0 e7 99 fc ef   ^.;.g.W.........
    0060 - a9 b5 72 c2 3b 2d b2 88-10 0c 1c 9f 0b 32 60 b1   ..r.;-.......2`.
    0070 - 5b 1b d4 07 7b b5 39 69-ba 70 84 bd fd ac 22 75   [...{.9i.p...."u
    0080 - bd 34 12 66 33 55 0c c4-2e 7f 7b 40 f0 28 ab eb   .4.f3U....{@.(..
    0090 - 68 58 5f e6 30 b8 5e 36-3f b9 0b ec 99 7f 0a 23   hX_.0.^6?......#
    00a0 - fa 2e a6 43 c1 22 a3 42-d5 a5 ba 81 9f 08 16 e1   ...C.".B........
    00b0 - 11 22 1a d7 e9 d0 30 eb-84 30 b4 66 b8 b0 55 26   ."....0..0.f..U&
    00c0 - 20 de ce 79 d2 4a 16 ef-5b 7c 0f d1 20 76 8d 7c    ..y.J..[|.. v.|
    00d0 - 23 46 3b ba 52 41 32 8e-d9 1e 00 40 cf 99 fb 19   #F;.RA2....@....
    00e0 - 11 57 3b 15 6e 11 2b 69-3b 66 b8 0d 25 7b 01 4c   .W;.n.+i;f..%{.L

    Start Time: 1629850720
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: 94D2D4E6AF0537B7954B0598F85D0CB83DD0867A723BEBB3DB2049DAE5732D14
    Session-ID-ctx: 
    Resumption PSK: 2450AA5D903DCB94D409C7DB761892864AA712372CB373022321247D83178F6BB520E6B43D0D0154E92766CC16806391
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 28 c6 3d 71 b3 5c 60 1f-a8 7d 5f f7 26 3c cd f5   (.=q.\`..}_.&<..
    0010 - 2e 43 d1 89 25 84 e5 dd-91 82 37 ad b9 95 13 3d   .C..%.....7....=
    0020 - de b5 14 d6 16 f3 d0 24-e0 b9 14 c2 87 b8 ce ed   .......$........
    0030 - a9 0e cd 1f 9e 3a 8a fe-b8 a2 14 e4 5d 1d e9 5b   .....:......]..[
    0040 - e4 18 22 e7 c5 61 de fc-9b 02 66 72 1f fb 89 b4   .."..a....fr....
    0050 - c5 00 4d d9 0d 15 49 e3-6c 4f 9e ad 99 ea 10 fb   ..M...I.lO......
    0060 - f6 b8 83 9f 29 59 59 c6-69 b3 35 cb 1f 19 3a ff   ....)YY.i.5...:.
    0070 - c2 53 af db 84 e0 07 6e-fc 26 59 b7 9c 1b 79 f1   .S.....n.&Y...y.
    0080 - 14 77 9d bb d4 c6 3c 90-39 9f 94 dc f8 ab 51 df   .w....<.9.....Q.
    0090 - 52 59 cc 13 47 ac 68 11-7c 24 c9 d9 54 3d 0b 88   RY..G.h.|$..T=..
    00a0 - bd 19 47 f1 e7 57 d4 6f-ad 61 71 68 6e e4 37 2c   ..G..W.o.aqhn.7,
    00b0 - c5 ac 45 b1 a1 1a ab 38-c0 2d 51 39 86 2f b7 fb   ..E....8.-Q9./..
    00c0 - 5b 4d 57 b8 19 94 da 6c-45 7d 24 23 b6 08 10 6a   [MW....lE}$#...j
    00d0 - 76 10 31 1a 91 c2 98 80-bb 49 07 88 65 df ea 24   v.1......I..e..$

    Start Time: 1629850720
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK

Revocation with OSCP Responder

Stapling usually has a validity of 7 days, where the server caches the response and no longer contacts the OCSP to obtain signed confirmation of the validity of its certificate, and immediatly sends the previous response to the client.
This means that if we revoke the certificate now, only after 7 days will the customer refuse the WebServer’s certificate.
Unless, of course, the WebServer is restarted and loses any memory of the OCSP response …
Let’s revoke the WebServer’s certificate:

1
2
3
4
5
6
stamp=$(date +%F_%T)
mv /root/ca/inter-ca/certs/www.tjs.lan.cert.pem{,-revoked_$stamp}
openssl ca -config /root/ca/inter-ca/openssl.cnf \
	-passin file:/root/ca/inter-ca/private/inter-ca.key.pass \
    -revoke /root/ca/inter-ca/certs/www.tjs.lan.cert.pem-revoked_$stamp \
	-crl_reason superseded

Using configuration from /root/ca/inter-ca/openssl.cnf
Revoking Certificate 1007.
Data Base Updated

Restart the ocsp service, so that it updates itself with the new CRL

1
systemctl restart ocsp-responder_inter-ca.service

Restart the nginx service on www, to drop the OCSP Stapling cache

1
systemctl restart nginx
1
echo QUIT | sudo openssl s_client -connect www.tjs.lan:443 -CAfile ~/ca.cert.pem -status

CONNECTED(00000003)
depth=2 C = PT, ST = Lisboa, L = Lisboa, O = TiagoJoaoSilva, OU = TJS, CN = ca-tjs-1, emailAddress = bofh@tjs.lan
verify return:1
depth=1 C = PT, ST = Lisboa, O = TiagoJoaoSilva, OU = TJS, CN = inter-ca-tjs-2, emailAddress = bofh@tjs.lan
verify return:1
depth=0 C = PT, ST = Lisboa, L = Lisboa, O = Tiago Joao Silva, OU = TJS, CN = www.tjs.lan
verify return:1
OCSP response: no response sent

Check that clients read the certificate as revoked

When we try to load www.tjs.lan on Firefox, we’re told that the certificate was revoked:

Issue a new certificate to replace the revoked one

If you are following the tutorial in order, it’s best to issue a new certificate for www.tjs.lan before proceeding.

Don’t forget that since the key was not really compromised nor has the www server information changed, your CSR remains valid, and so it’s only necessary to repeat the steps from the CSR signature onwards. If the private key had been compromised, it would be necessary to issue a new certificate for www from scratch.

Put it in the appropriate place on www.tjs.lan, restart NGINX, and test again if the client can access the server’s webpage:

Revoke the IntermediateCA

To revoke the IntermediateCA (let’s assume that someone left the private key of the IntermediateCA in a public GitHub repository), we have to go back to the RootCA, which is offline, and follow the steps described in the corresponding section:

1
2
3
4
5
6
stamp=$(date +%F_%T)
mv /root/ca/inter-ca/certs/inter-ca.cert.pem{,-revoked_$stamp}
openssl ca -config /root/ca/openssl.cnf \
	-passin file:/root/ca/private/ca.key.pass \
    -revoke /root/ca/inter-ca/certs/inter-ca.cert.pem-revoked_$stamp \
	-crl_reason keyCompromise

Using configuration from /root/ca/openssl.cnf
Revoking Certificate 1003.
Data Base Updated

Update the CRL and convert it to DER, following the previously shown steps:

1
2
3
4
5
6
openssl ca -config /root/ca/openssl.cnf -gencrl \
	-passin file:/root/ca/private/ca.key.pass \
	-keyfile /root/ca/private/ca.key.pem -cert /root/ca/certs/ca.cert.pem \
	-out /root/ca/crl/ca.crl.pem
openssl crl -inform PEM -in /root/ca/crl/ca.crl.pem \
	-outform DER -out /root/ca/crl/root-ca.crl

You can see the updated CRL has a new entry:

1
openssl crl -inform DER -text -noout -in /root/ca/crl/root-ca.crl

Certificate Revocation List (CRL):
        Version 2 (0x1)
        Signature Algorithm: sha512WithRSAEncryption
        Issuer: C = PT, ST = Lisboa, L = Lisboa, O = TiagoJoaoSilva, OU = TJS, CN = ca-tjs-1, emailAddress = bofh@tjs.lan
        Last Update: Aug 25 20:50:52 2021 GMT
        Next Update: Sep 14 20:50:52 2022 GMT
        CRL extensions:
            X509v3 Authority Key Identifier:
                keyid:01:F2:CC:54:E0:F2:58:AC:E2:14:8E:2B:DB:6D:B6:FF:5C:25:41:A0

            X509v3 CRL Number:
                4098
Revoked Certificates:
    Serial Number: 1001
        Revocation Date: Aug 13 22:11:25 2021 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code:
                Unspecified
    Serial Number: 1002
        Revocation Date: Aug 13 23:41:51 2021 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code:
                Superseded
    Serial Number: 1003
        Revocation Date: Aug 25 20:45:54 2021 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code:
                Key Compromise

Bring the updated CRL from offline to online

Using a safe medium that doesn’t break the airgapping of the RootCA’s system, take the CRL file to the server hosting the RootCA’s CRLDistribution:

1
2
cp root-ca.crl /var/www/crl
systemctl restart lighttpd.service

To empty the OCSP Stapling’s cache, restart the webserver if we want the revocation to be immediately recognized:

1
systemctl restart nginx.service

Test the IntermediateCA’s revocation

On modern browsers (fails)

The site is still considered trusted, because apparently browsers do not bother to check the entire chain of certificates (not even by OCSP), relying only on the internal lists of RootCAs and CRLs distributed with browser updates.
The only exception seemed to be good old Internet Explorer, may it rest in pieces, which we’ll try right away.
https://news.netcraft.com/archives/2013/05/13/how-certificate-revocation-doesnt-work-in-practice.html
And I can’t find an openssl verify command that can indicate that the certificate chain www.tjs.lan.cert_chain.pem is broken by the revocation of the IntermediateCA
(https://stackoverflow.com/questions/25482199/verify-a-certificate-chain-using-openssl-verify)
PKI usage in browsers takes many shortcuts, or so it seems…

On Internet Explorer

To test with Internet Explorer, we need a Windows client, so I provisioned a Windows 7 VM.

Import the RootCA’s Certificate:

Bring the RootCA’s certificate to the Windows client and save it as ca.cert.cer)

.CER is the extension Windows expects for PEM (Base64) files

Win+R, certmgr.msc
Select Trusted Root Certification Authorities/Certificates
All Tasks > Import…

Pick the certificate (Browse…)

Confirm that we want to install in Trusted Root

Yes

And it’s installed.

hosts file

Now we need to put the same information in the hosts file because we still don’t have DNS:
Open Notepad as Administrator (right-click on Notepad, Run as administrator)

Open the %SystemRoot%\System32\drivers\etc\hosts file
Paste:

#tjs.lan
192.168.1.70		server.tjs.lan 	server
192.168.1.70		ocsp.tjs.lan	ocsp
192.168.1.77		www.tjs.lan 	www

Save the file and close Notepad.

Opening our site on IE

Now we can test if IE checks whether the InterCA that signed the certificate of our site was revoked:
And the answer is yes, Internet Explorer queries and respects an internal PKI, instead of just trusting the public PKI information used on the Internet at large.