OpenSSL PKI (part 2) - IntermediateCA
Creating an IntermediateCA
Once again, the intermediateCA(s) must be hosted in a separate system (server) from that which hosts the RootCA, and the RootCA’s system (root) should be airgapped and offline.
In the online system I don’t think it’s a bad idea to keep the secondary CAs under /root/ca
, which allows the placing the RootCA’s certificate on the same path (/root/ca/certs/ca.cert.pem
). However, this is the only information from the RootCA that needs to be brought to the online world where the secondary CAs reside; it is even debatable if it is worth bringing the RootCA’s CRL in PEM format and putting it in /root/ca/crl/ca.crl.pem
.
|
|
Create the complete folder tree that is needed to support the IntermediateCA’s operations:
|
|
Create the configuration file for the InterCA:
|
|
In an ideal world, there would be several subCAs, named by the use to which they would be intended (usually with different extension sets for each use):
- tls-ca: Certificates for servers and clients
- vpn-ca: Certificates for VPNs
- mime-ca: Certificates for email
- code-ca: Certificates for code signing
- net-ca: Certificates for network assets
The configuration of these extensions is outside the scope of this document. But each of them would have their own folder tree and their copy of theopenssl.cnf
, to which I would give a more specific name in each case.
Actually, it seems that almost no one bothers to do all this; there’s usually a separate CA for external VPNs, if the company develops software there’s a CA for code-signing, and then a single CA for all internal systems.
To avoid having to create separate configuration files for each certificate that re-specify all the functionalities that are common to the InterCA, we add the following to/root/ca/inter-ca/openssl.cnf
:
[ CA_default ]
(…)
# copy extensions missing from the CSR to the final certificate
copy_extensions = copy
Note that since this IntermediateCA is more dynamic than the RootCA, its CRL’s validity should not be greater than 30 days; I’d advise you to set up a periodic task that refreshes the CRL every 29 days.
default_crl_days = 30
Generate a complex password for the InterCA:
|
|
We create the InterCA’s key providing the complex password:
|
|
Generating RSA private key, 8192 bit long modulus (2 primes) ......................................................................................................................................................................................................+++ .....................................................................................................................................................................................................+++ e is 65537 (0x010001)
|
|
Create the Certificate Signing Request that will be signed by the RootCA (notice that it is placed on the csr
folder of the RootCA):
|
|
You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [PT]: State or Province Name [Lisboa]: Locality Name [Lisboa]: Organization Name [TiagoJoaoSilva]: Organizational Unit Name [TJS]: Common Name []:**inter-ca-tjs-2** Email Address [bofh@tjs.lan]
Sign the CSR with the RootCA
Go back to the offline system of the RootCA and add some folder for the IntermediateCA.
As it will only support the creation of the InterCA(s) and not its operation, it is much simpler:
|
|
Bring in from the online system the IntermediateCA’s CSR, which we place on the same path where it was (the RootCA’s csr
folder):
|
|
To make sure that the CDL and/or AIA information of the RootCA is passed to the IntermediateCA, we add this directive to the [ CA_default ]
section on /root/ca/openssl.cnf
# copy extensions missing from the CSR to the final certificate
copy_extensions = copy
Sign the CSR; notice that we specify the extension from /root/ca/openssl.cnf
to be used is [ v3_intermediate_ca ]
!
|
|
Using configuration from /root/ca/openssl.cnf Check that the request matches the signature Signature ok Certificate Details: Serial Number: 4099 (0x1003) Validity Not Before: Aug 13 23:44:07 2021 GMT Not After : Aug 11 23:44:07 2031 GMT Subject: countryName = PT stateOrProvinceName = Lisboa organizationName = TiagoJoaoSilva organizationalUnitName = TJS commonName = inter-ca-tjs-2 emailAddress = bofh@tjs.lan X509v3 extensions: X509v3 Subject Key Identifier: E3:75:25:D7:74:08:0B:23:F0:AF:E4:EC:D0:D4:52:CB:5A:3A:10:2E 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 Basic Constraints: critical CA:TRUE, pathlen:0 X509v3 Key Usage: critical Digital Signature, Certificate Sign, CRL Sign X509v3 CRL Distribution Points: Full Name: URI:http://server.tjs.lan:7788/root-ca.crl Certificate is to be certified until Aug 11 23:44:07 2031 GMT (3650 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
We check the signing:
|
|
/root/ca/inter-ca/certs/inter-ca.cert.pem: OK
Create a certificate for the Chain of Authority with a Chain certificate (also called Bundle), by concatenating the certificates in ascending order - the latter will be the RootCA or the nearest to it.
|
|
Activate the IntermediateCA on the online system
Since we left on the online system all the things the IntermediateCA needs to do its job, we only have to bring the certificates signed by the RootCA to their proper place (using safe methods); I didn’t find any guidance whether the IntermediateCA’s certificates should be placed in /root/ca/certs
(as it would be logical to do), so I’ll place them in /root/ca/inter-ca/certs
|
|
Revoking IntermediateCA certificates
As the number of certificates to issue (or revoke) by the IntermediateCA will be much larger than the RootCA’s, it’s better to use the OCSP Responder method (also called AIA because of the directive AuthorityInfoAccess that sets it up), to check for certificate revocation.
Setting up the OCSP Responder’s address on the InterCA’s CNF
We start by putting the following in the [server_cert]
and [usr_cert]
sections of /root/ca/inter-ca/openssl.cnf
authorityInfoAccess = OCSP;URI:http://ocsp.tjs.lan:7888
Create the certificate
Generate a key:
|
|
Generating RSA private key, 4096 bit long modulus (2 primes) ...............................++++ ................................................++++ e is 65537 (0x010001)
Insert the OCSP-specific SAN (Subject Alternative Name) informations on another .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 = ocsp.tjs.lan
[req_ext]
subjectAltName = @alt_names
[alt_names]
IP.1 = 192.168.1.70
DNS.1 = ocsp.tjs.lan
Build the CSR with that SAN info:
|
|
Confirm the CSR has the SAN info:
|
|
X509v3 Subject Alternative Name: IP Address:192.168.1.70, DNS:ocsp.tjs.lan
Sign the CSR with
-extensions ocsp
:
|
|
Using configuration from /root/ca/inter-ca/openssl.cnf Check that the request matches the signature Signature ok Certificate Details: Serial Number: 4099 (0x1003) Validity Not Before: Aug 22 22:53:56 2021 GMT Not After : Nov 23 22:53:56 2023 GMT Subject: countryName = PT stateOrProvinceName = Lisboa localityName = Lisboa organizationName = Tiago Joao Silva organizationalUnitName = TJS commonName = ocsp.tjs.lan X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Subject Key Identifier: BD:A6:EE:6D:0F:73:0E:47:DC:26:9A:A8:B0:40:18:55:DB:35:6B:5A 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 X509v3 Key Usage: critical Digital Signature X509v3 Extended Key Usage: critical OCSP Signing Certificate is to be certified until Nov 23 22:53:56 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
Confirm the certificate has the OCSP extension info:
|
|
X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Subject Key Identifier: BD:A6:EE:6D:0F:73:0E:47:DC:26:9A:A8:B0:40:18:55:DB:35:6B:5A 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 -- X509v3 Key Usage: critical Digital Signature X509v3 Extended Key Usage: critical OCSP Signing
|
|
Use OpenSSL to create a testing OCSP Responder (do not use in production)
For testing, we’ll activate OpenSSL’s in-built OCSP Responder temporarily:
- we’ll use port 7888, one of the previously known free ports (see Part 1)
- with the
-nrequest 1
option, it wil close itself after a single query - and we’ll gather a log.
|
|
ocsp: waiting for OCSP client connections...
Open a port on the firewall
|
|
Query the Responder about a certificate (the Responder’s own, or another):
|
|
[…] Response verify OK /root/ca/inter-ca/certs/ocsp.tjs.lan.cert.pem: good This Update: Aug 22 23:06:21 2021 GMT [1]+ Done openssl ocsp -index /root/ca/inter-ca/index.txt -port 7888 -text -CA /root/ca/inter-ca/certs/inter-ca_chain.cert.pem -rkey /root/ca/inter-ca/private/ocsp.tjs.lan.key.pem -rsigner /root/ca/inter-ca/certs/ocsp.tjs.lan.cert.pem -out /root/ca/inter-ca/ocsp-log.txt -crl_check_all -nrequest 1
Examine the log to check what’s happened:
|
|
OCSP Request Data: Version: 1 (0x0) Requestor List: Certificate ID: Hash Algorithm: sha1 Issuer Name Hash: AE59C598DC56A005EA5239AE471093B7924F5018 Issuer Key Hash: E37525D774080B23F0AFE4ECD0D452CB5A3A102E Serial Number: 1003 Request Extensions: OCSP Nonce: 04104CA2F5C63FA8F6A8D89256935DB37177 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 22 23:06:21 2021 GMT Responses: Certificate ID: Hash Algorithm: sha1 Issuer Name Hash: AE59C598DC56A005EA5239AE471093B7924F5018 Issuer Key Hash: E37525D774080B23F0AFE4ECD0D452CB5A3A102E Serial Number: 1003 Cert Status: good This Update: Aug 22 23:06:21 2021 GMT Response Extensions: OCSP Nonce: 04104CA2F5C63FA8F6A8D89256935DB37177 Signature Algorithm: sha256WithRSAEncryption
Set the OCSP Responder up as a permanent service
If your distribution uses systemd, create a Service Unit (other inits have their own ways)
|
|
[Unit]
Description = OCSP responder using OpenSSL (for CA inter-ca)
After = network.target
[Service]
ExecStart = /usr/bin/openssl ocsp \
-index /root/ca/inter-ca/index.txt \
-port 7888 \
-rkey /root/ca/inter-ca/private/ocsp.tjs.lan.key.pem \
-rsigner /root/ca/inter-ca/certs/ocsp.tjs.lan.cert.pem \
-CA /root/ca/inter-ca/certs/inter-ca_chain.cert.pem \
-text -crl_check_all\
-out /root/ca/inter-ca/ocsp-log.txt
[Install]
WantedBy = multi-user.target
|
|
Test the service:
|
|
● ocsp-responder_inter-ca.service - OCSP responder using OpenSSL (for CA inter-ca) Loaded: loaded (/etc/systemd/system/ocsp-responder_inter-ca.service; disabled; vendor preset: enabled) Active: active (running) since Mon 2021-08-23 00:21:27 WEST; 22s ago […] Aug 23 00:21:27 debian systemd[1]: Started OCSP responder using OpenSSL (for CA inter-ca). Aug 23 00:21:27 debian openssl[2585]: ocsp: waiting for OCSP client connections...
Query the service:
|
|
Confirm that the answer is identical to the previously done test. Then leave the service as permanent:
|
|
Other CAs
If this is not the only IntermediateCA, others could be set up following exactly the same steps, and just changing the listening port (they’re free up to 7899/TCP, so 10 more potential ports).
However, it is not recommended to use OpenSSL as an OCSP Responder, because the implementation is barebone.