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.
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
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.
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.
I hate blogs with pop-ups, ads and all the (even worse) other stuff that distracts from the topics you're reading and violates your privacy. I want to offer my readers the best experience possible for free, ... but please be wary that for me it's not really free: on top of the raw costs of running the blog, I usually spend on average 50-60 hours writing each post. I offer all this for free because I think it's nice to help people, but if you think something in this blog has helped you professionally and you want to give concrete support, your contribution is very much appreciated: you can just use the above button.