In the "OpenSSL CA tutorial - a full-featured OpenSSL PKI" post we set-up a full featured Public Key Infrastructure with Root and Intermediate Certificate Authorities, Indirect CRL and OCSP Responders. To have a go with that PKI, we also generated an Extended Validation (EV) certificate ("/tmp/foo.crt").

In this post we are using the same PKI we set up in that post, the EV certificate we generated and we also generate a new Organization Validated (OV) certificate: the goal this time is showing how to deal with Indirect CRL generation, CRL validation and OCSP validation.

If you want to try yourself the examples of this post, you need to read the "OpenSSL CA tutorial - a full-featured OpenSSL PKI" and set up all the labs shown there.

Generate An Organization Validated (OV) Certificate

Generate the certificate's private key as follows:

openssl ecparam -name secp384r1 -genkey \
| openssl ec -out /tmp/bar.key -aes256

then, generate the Certificate Signing Request (CSR) - in this example we are requesting the issuing a certificate with the following attributes:

  • Country: CH
  • Organization: Carcano SA
  • Common Name: bar.org
  • Subject Alternatives Names:
    • bar.org
    • www.bar.org
    • mail.bar.org
    • 10.1.32.14
openssl req -new -key /tmp/bar.key -out /tmp/bar.csr \
-subj "/C=CH/O=Carcano SA/CN=bar.org" \
-addext "subjectAltName = DNS:bar.org, DNS:www.bar.org, DNS:mail.bar.org, IP:10.1.32.14"

Submit the CSR to the CARC I100T Intermediate CA to get back the signed certificate.

In this example we are requesting a certificate valid for 397 days:

CA_LABEL="carc_i100t"
PKI_CONF_PATH="/etc/corporate-pki/ca/${CA_LABEL}"
openssl ca -batch -config ${PKI_CONF_PATH}/conf/openssl.conf \
-extensions ov -days 397 -in /tmp/bar.csr -out /tmp/bar.crt

let's have a closer look to the certificate:

openssl x509 -noout -text -in /tmp/bar.crt 

it must look like as follows:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 4099 (0x1003)
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = CH, O = Carcano SA, CN = CARC I100T
        Validity
            Not Before: Jun 21 20:53:55 2023 GMT
            Not After : Jul 22 20:53:55 2024 GMT
        Subject: C = CH, O = Carcano SA, CN = bar.org
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (384 bit)
                pub:
                    04:b8:d4:45:56:2d:93:a8:2e:4f:2c:7b:b8:70:7d:
                    2a:48:6f:4d:bc:67:62:18:db:0c:6b:4c:9b:c5:e3:
                    d7:12:28:52:8f:9f:ec:75:e2:15:e0:de:70:25:72:
                    da:fa:8a:90:76:80:a6:2e:a2:dc:53:53:b7:75:98:
                    be:cc:6c:7d:3c:39:f3:2c:80:9c:30:43:4c:5d:c4:
                    14:f3:cc:62:39:89:32:7e:d5:42:6d:48:dd:98:f1:
                    20:be:70:24:70:2a:bf
                ASN1 OID: secp384r1
                NIST CURVE: P-384
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                87:28:9B:98:E9:B0:6A:FF:27:2B:81:69:AF:C2:D5:F4:A0:71:32:FD
            X509v3 Authority Key Identifier: 
                BF:04:DD:5A:B5:0C:97:31:D5:27:CA:9C:A8:A2:F5:F4:8A:AD:39:91
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: critical
                TLS Web Server Authentication
            X509v3 CRL Distribution Points: 
                Full Name:
                  URI:http://www.test.carcano.local/crl/carc_i100t.crl                CRL Issuer:
                  DirName:C = CH, O = Carcano SA, CN = CARC I100T
            Authority Information Access: 
                OCSP - URI:http://ocsp.test.carcano.local:8890
                CA Issuers - URI:http://ca.test.carcano.local/carc_i100t.cer
            X509v3 Certificate Policies: 
                Policy: 1.3.6.1.4.1.1234.1.1
                  CPS: http://www.carcano.local/ca/cps.html
                  User Notice:
                    Explicit Text: CARC I100T Certification Practice Statement
                Policy: 2.23.140.1.2.2
                  User Notice:
                    Explicit Text: Compliant with Baseline Requirements – Individual identity asserted
    Signature Algorithm: ecdsa-with-SHA256
    Signature Value:
        30:81:87:02:41:5f:f6:d4:b4:0b:e4:97:df:29:9b:8f:d6:81:
        a6:d9:94:f5:b1:74:c7:3c:a0:8f:43:ef:3b:91:a8:e7:19:45:
        d9:44:95:6e:d5:d5:67:fa:e2:14:57:79:e0:72:ca:c0:2f:8f:
        07:ac:a1:84:fe:77:ec:08:b1:3b:9e:00:57:49:8e:e6:02:42:
        01:74:db:26:17:d4:1d:9f:97:d6:58:3c:92:44:6e:de:ff:c0:
        2f:f3:d0:53:8f:72:5d:b6:b3:e0:9d:0c:a8:23:0c:fa:d4:9f:
        e1:74:e8:7e:9c:51:2c:bf:6d:f3:b8:f3:d6:96:dc:a3:e2:19:
        6a:47:73:63:cd:4b:d9:7e:a3:9a:af:81

Generate The Indirect CRL

We are ready to generate the Indirect Certificate Revocation List (CRL); the statement is the same as the one you'd use for generating a regular CRL.

openssl ca -config ${PKI_CONF_PATH}/conf/crl.conf \
-gencrl -crlexts crl_extensions \
-out /var/lib/corporate-pki/${CA_LABEL}/crl/crl.pem

the output is as follows:

Using configuration from /etc/corporate-pki/ca/carc_i100t/conf/crl.conf

We can have a look to the generated CRL - type the following statement:

openssl crl -noout -text -in /var/lib/corporate-pki/${CA_LABEL}/crl/crl.pem

the output is as follows:

Certificate Revocation List (CRL):
        Version 2 (0x1)
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = CH, O = Carcano SA, CN = CARC I100T
        Last Update: Jun 21 20:55:15 2023 GMT
        Next Update: Jul 21 20:55:15 2023 GMT
        CRL extensions:
            X509v3 Authority Key Identifier: 
                C0:1E:78:99:17:F8:39:A0:DB:A1:0F:95:33:35:15:17:D9:2E:15:E8
            X509v3 Issuing Distribution Point: 
                Full Name:
                  URI:http://www.test.carcano.local/crl/carc_i100t.crl                Indirect CRL

            X509v3 CRL Number: 
                4096
No Revoked Certificates.
    Signature Algorithm: ecdsa-with-SHA256
    Signature Value:
        30:66:02:31:00:f6:8c:7c:af:14:e0:94:0d:3a:43:7e:e4:12:
        d8:06:a7:23:78:03:05:ab:c8:d7:8e:66:87:a7:12:d5:b6:83:
        64:73:c9:3b:aa:43:ec:c9:d0:dc:82:4b:23:80:dc:c9:99:02:
        31:00:f1:05:78:cf:14:01:9b:ae:e2:79:29:55:94:2a:17:f7:
        21:3d:00:0b:c1:0e:df:cf:b4:0c:27:a1:89:44:5c:95:cc:83:
        8a:47:ca:01:a8:6c:50:84:e6:b2:43:d6:aa:a9
Have a close look at the above output: the CRL is marked as "Indirect CRL". As we saw in the "OpenSSL CA tutorial - a full-featured OpenSSL PKI" post, the advantage of indirect CRL is that the CA certificate is not needed for signing the CRL: this makes it much easier to keep CA's private keys offline.

What enabled the CRL to be indirect are the settings of the Certification Authority configuration file (openssl.conf - see the "OpenSSL CA tutorial - a full-featured OpenSSL PKI" post) that:

  • claim the DirName in the CRL Distribution Point x509v3 extension using the same subject DN of the CA's certificate itself
  • enable to issue a certificate with CRLSign key usage

in addition to that, the private key of the certificate used for signing the CRL has the same subject DN of the CA's certificate itself (of course matching the DirName attribute claimed in the CRL Distribution Point x509v3 extension)

As for the Validation Authority (VA), its configuration file (crl.conf) is as follows:

[ ca ]
default_ca = CA_default

[ CA_default ]
dir               = /var/lib/corporate-pki/carc_i100t
private_key       = /etc/corporate-pki/ca/carc_i100t/keys/va.key
certificate       = /etc/corporate-pki/ca/carc_i100t/certs/va.crt
database          = $dir/db/index.txt
default_md        = sha256
crlnumber         = $dir/db/crl_serial
default_crl_days  = 30
crl_dir           = /var/lib/corporate-pki/carc_i100t/crl
crl               = /var/lib/corporate-pki/carc_i100t/crl/carc_i100t

[ crl_extensions ]
authorityKeyIdentifier = keyid:always
issuingDistributionPoint = @crl_issuing_dp

[ crl_issuing_dp ]
fullname = URI:http://www.test.carcano.local/crl/carc_i100t.crl
indirectCRL=TRUE

As you see, it:

  • set the VA's private key and certificate as the ones to be used when signing the CRL - lines 6-7
  • claims the CRL Distribution Points using the same URL set in the CA's configuration file - line 20
  • claims the CRL as indirect (indirectCRL = TRUE) - line 21

Validating The Certificate's Revocation Status

We now have both the Indirect CRL and the OCSP responder: we can finally have a go validating the certificate's revocation status.

Validate A Certificate Verifying Also The CRL

The simplest validation of a certificate is just running the "openssl verify" statement followed by the path to the certificate's file - for example:

openssl verify -CAfile ${PKI_CONF_PATH}/certs/bundle.crt /tmp/foo.crt

this validates the certificate itself in the most basic way, making sure :

  • it is issued by a trusted CA contained in the file specified by the -CAfile option
  • that its lifetime is still valid

but it does not check if the certificate has been revoked.

Checking the revocation status requires to specify also the following parameters:

  • -crl_check, to enable the checking of t he CRL
  • -CRLfile, with the path to the file containing the CRL

Since this is an indirect CRL, we must also specify:

  • -extended_crl, enables extend CRL validation, such as indirect CRL and alternate CRL signing keys
  • -untrusted, with the path to the certificate used to sign the CRL, since it must be validated as well
openssl verify -CAfile ${PKI_CONF_PATH}/certs/bundle.crt \
-crl_check -extended_crl \
-CRLfile /var/lib/corporate-pki/${CA_LABEL}/crl/crl.pem \
-untrusted ${PKI_CONF_PATH}/certs/va.crt \
/tmp/foo.crt

the outcome must be as follows:

/tmp/foo.crt: OK

Validate using the OCSP Responder

As we saw in the "X509 Certificates HowTo – A Public Key Infrastructure Tutorial" post, OCSP is a more modern and efficient way for validating certificate status, since it provides an online responder that can be queried, getting back a simple valid or revoked answer along with the revocation reason, instead of a list of all the revoked certificates, requiring the client looking inside it to see if the certificate to be verified is contained into it.

The OCSP responder can be queried as follows: 

openssl ocsp -CAfile /etc/corporate-pki/ca/carc_r100t/certs/ca.crt \
-issuer ${PKI_CONF_PATH}/certs/ca.crt \
-VAfile ${PKI_CONF_PATH}/certs/va.crt \
-url http://ocsp.test.carcano.local:8890 \
-cert /tmp/foo.crt

as you see you must explicitly provide:

  • the ROOT CA's certificate file (-CAfile option)
  • the Intermediate CA's certificate file (-issuer option)
  • the Validation Authority's certificate file (-VAfile option)
  • the URL of the OCSP responder (it is the one specified by the OCSP field in the Authority Information Access )
  • since this VA is using a signed by the Intermediate CA (instead of the Root CA), there is a mismatch in the Authority Key Identifier that can be addressed only providing the -noverify option

The outcome must be as follows:

/tmp/foo.crt: good
	This Update: Jun  1 15:57:36 2023 GMT

of course "good" means the certificate has not still been revoked yet.

It may happen that when verifying the OCSP status you get the following response: "/tmp/foo.crt: unknown": this is because the OCSP instance did not realize the index file changed: the work-around is just restarting the OCSP Responder service (systemctl restart ocsp@carc_i100t) - As already said in the previous post, the OpenSSL OCSP implementation is intended to be used in Labs only. If you need something more reliable, consider using OpenXPKI or Cloudflare's PKI and TLS Toolkit. If you are interested into quickly learn how to set up a full-featured PKI with Cloudflare's PKI and TLS Toolkit, here are the links to the book I wrote on it: ebook and of course, paperback.
If you fancy to see all the OCSP text, add the -resp_text option.

Revoke

Let's see what happens when revoking a certificate - let's revoke the "bar" certificate, claiming key compromisation as the  revocation reason:

openssl ca -config ${PKI_CONF_PATH}/conf/openssl.conf \
-crl_reason keyCompromise -revoke /tmp/bar.crt

the output must be as follows:

Using configuration from /etc/corporate-pki/ca/carc_i100t/conf/openssl.conf
Revoking Certificate 1002.
Data Base Updated

as we already saw, with OpenSSL it is necessary to restart the OCSP responder of the CARC I100T CA too, so to have it reading the index file again:

systemctl restart ocsp@carc_i100t

Generate a new CRL

Conversely from OCSP, that directly relies on the index file, working with CRL requires re-generating  the CRL so to have the freshly revoked certificate included in it.

Generate a new CRL as follows:

openssl ca -config ${PKI_CONF_PATH}/conf/crl.conf \
-gencrl -crlexts crl_extensions \
-out /var/lib/corporate-pki/${CA_LABEL}/crl/crl.pem

the output is as follows:

Using configuration from /etc/corporate-pki/ca/carc_i100t/conf/crl.conf

let's have a look to the new CRL:

openssl crl -noout -text -in /var/lib/corporate-pki/${CA_LABEL}/crl/crl.pem

the output must be as follows:

Certificate Revocation List (CRL):
        Version 2 (0x1)
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = CH, O = Carcano SA, CN = CARC I100T
        Last Update: Jun 21 21:02:12 2023 GMT
        Next Update: Jul 21 21:02:12 2023 GMT
        CRL extensions:
            X509v3 Authority Key Identifier: 
                C0:1E:78:99:17:F8:39:A0:DB:A1:0F:95:33:35:15:17:D9:2E:15:E8
            X509v3 Issuing Distribution Point: 
                Full Name:
                  URI:http://www.test.carcano.local/crl/carc_i100t.crl                Indirect CRL

            X509v3 CRL Number: 
                4097
Revoked Certificates:
    Serial Number: 1002
        Revocation Date: Jun 21 21:01:50 2023 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code: 
                Key Compromise
    Signature Algorithm: ecdsa-with-SHA256
    Signature Value:
        30:66:02:31:00:a8:8f:a5:dc:1f:13:e4:f3:9a:be:46:9e:d9:
        bf:12:4b:78:71:87:4d:6a:5a:2d:c0:ac:7b:5b:73:6b:dc:cd:
        50:f0:a4:05:cf:61:eb:2d:56:6e:87:ad:66:f0:88:29:d3:02:
        31:00:bb:ca:71:ce:9d:91:23:e9:13:ea:95:0d:b0:25:1f:25:
        f3:e8:17:1f:15:5c:fa:b2:a3:05:df:de:ea:f7:67:52:72:52:
        4e:5d:24:cd:6f:42:a8:86:43:ff:7c:7f:8d:da

as you see, conversely from the previous CRL, this contains Revoked Certificates - the only one is the certificate with serial number 1002, revoked because of key compromise.

Validating The Certificate

Validate Using the CRL

Let's verify the bar's certificate revocation status using the freshly issued CRL:

openssl verify -CAfile ${PKI_CONF_PATH}/certs/bundle.crt \
-crl_check -extended_crl \
-CRLfile /var/lib/corporate-pki/${CA_LABEL}/crl/crl.pem \
-untrusted ${PKI_CONF_PATH}/certs/va.crt \
/tmp/bar.crt

as expected, it is claimed as revoked.

C = CH, O = Carcano SA, CN = bar.org
error 23 at 0 depth lookup: certificate revoked
error /tmp/bar.crt: verification failed

as you see, CRL verification does not provide information about the revocation reason.

Validating Using the OCSP Responder

Let's verify the bar's certificate revocation status using the OCSP responder:

openssl ocsp \
-CAfile /etc/corporate-pki/ca/carc_r100t/certs/ca.crt \
-issuer /etc/corporate-pki/ca/carc_i100t/certs/ca.crt \
-VAfile ${PKI_CONF_PATH}/certs/va.crt \
-url http://ocsp.test.carcano.local:8890 \
-cert /tmp/bar.crt

again, as expected, it is claimed as revoked.

/tmp/bar.crt: revoked
	This Update: Jun  1 16:08:48 2023 GMT
	Reason: keyCompromise
	Revocation Time: Jun  1 16:00:08 2023 GMT

but, conversely from CRL, OCSP also provided the revocation reason (keyCompromise).

Footnotes

Here it ends this post dedicated to certificate revocation status validation: besides learning how to deal with OCSP, we also saw how to generate and verify indirect CRL (as usual we saw the hardest scenario). As you see, despite OpenSSL providing such capabilities, it has not been designed to be used on production - for example it is necessary to restart the OCSP responder every time a certificate is issued or revoked.

A production grade product is Cloudflare's PKI and TLS Toolkit. If you are interested into quickly learn how to set up a full-featured PKI with Cloudflare's PKI and TLS Toolkit, here are the links to the book I wrote on it: ebook and of course, paperback.

Writing a post like this takes a lot of hours. I'm doing it for the only pleasure of sharing knowledge and thoughts, but all of this does not come for free: it is a time consuming volunteering task. This blog is not affiliated to anybody, does not show advertisements nor sells data of visitors. The only goal of this blog is to make ideas flow. So please, if you liked this post, spend a little of your time to share it on Linkedin or Twitter using the buttons below: seeing that posts are actually read is the only way I have to understand if I'm really sharing thoughts or if I'm just wasting time and I'd better give up

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>