- Prepare CA directory
- Create CA certificate
- Create a Certificate Revocation List
- Issue a client certificate
- Issue a server certificate
- Check a certificate against CRL
- Renew an expired certificate
- Revoke a valid certificate
Lets start by initialising the folder where our CA will reside and updating configurations to match your system:
-
Initialise the directory structure:
export OPENSSL_CONF="$(pwd)/openssl.cnf" touch index.txt index.txt.attr mkdir config private public openssl rand -hex -out serial 5
-
Copy
openssl-add-san
script somewhere in your path, eg:/usr/local/bin
-
Copy CA configuration file onto your system and update the following settings:
-
ca_default > dir
- this is the directory where the CA will reside (at this point this should be your current directory). -
ca_default > certs
- this is the directory whereopenssl ca
utility stores backups of all certificates it generates. This is targeted at larger and more complex CA setups. In a low volume / experimental CA this directory quickly fills up with never-used / test certificates. Thus, we point it at the Trash. -
req_distinguished_name > *_default
- un-comment/comment out appropriate items and add meaningful defaults within this section. -
authority_info_access > URI
- add a publicly accessible URI for yourca.cert.pem
file -
crl_distribution_points > fullname
- add a publicly accessible URI for yourca.crl.pem
file
-
-
Enable "Folder Actions" on macOS (A similar setup can be achieved on Linux using such tools as
fswatch
,inotify-tools
):- open
Automator
and create a newFolder Action
- attach this new Folder Action to the folder where your CA resides
- add "Filter Finder Items" action with the following conditions:
- Find files where ANY of the following conditions are true
- Name begins with "serial"
- Name begins with "index.txt"
- add "Move Finder Items to Bin" action
From now on, all new files created within the folder that match the above conditions will be automatically removed. The folder action does not apply to files that already exist in the directory (inc. renamed files).
- open
Once the maintenance job (aka Folder Action) is watching over our CA directory, we can proceed to creating a self-signed CA certificate:
-
Generate CA private key:
openssl ecparam -genkey -name secp384r1 | openssl ec -out private/key.pem -aes256 # openssl genpkey -algorithm ED25519 -aes256 -out private/key.pem
-
Create a Certificate Signing Request (CSR) for the new CA cert:
openssl req -new -key private/key.pem -out public/csr.pem openssl req -noout -text -verify -in public/csr.pem
-
Self-sign the above CA Certificate Signing Request:
openssl rand -hex -out serial 5 openssl ca -selfsign -extensions new_ca -startdate 190101000000Z -enddate 380118000000Z -in public/csr.pem -out public/cert.pem openssl x509 -noout -text -in ca.cert.pem
-
Backup CA private key and the certificate into a PFX bundle (optional):
openssl pkcs12 -export -inkey private/key.pem -in public/cert.pem -out private/backup.pfx
-
Add CA certificate to macOS System key chain:
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain public/cert.pem
Useful links:
- (Requirements for trusted certificates in iOS 13 and macOS 10.15)[https://support.apple.com/en-us/HT210176]
- (Year 2038 problem)[https://en.wikipedia.org/wiki/Year_2038_problem]
Once CA certificate is created, we can add a custom Certificate Revocation List and ensure that it works with certificate validation commands.
-
Create a new Certificate Revocation List
openssl rand -hex -out serial 5 openssl ca -updatedb openssl ca -gencrl -out public/crl.pem openssl crl -text -in public/crl.pem
-
Link certificates by their serial numbers
ln -sf cert.pem "public/$(openssl x509 -noout -subject_hash -in public/cert.pem).0" ln -sf crl.pem "public/$(openssl crl -noout -hash -in public/crl.pem).r0"
NOTE: Don't forget to upload the new CRL to the CRL endpoint defined in the configuration file (see
[ crl_distribution_points ]
).
Now that we have a functioning CA, we can create client certificates for our users in much the same way as we've created the CA cert.
NOTE: Remember to include your custom OpenSSL configuration file:
export OPENSSL_CONF="path/to/openssl.cnf"
-
Generate user private key:
openssl ecparam -genkey -name secp384r1 | openssl ec -out key.pem -aes256 # openssl genpkey -algorithm ED25519 -aes256 -out key.pem
-
Create a Certificate Signing Request (CSR) for the client cert:
openssl req -new -key key.pem -out csr.pem openssl req -noout -text -verify -in csr.pem
-
Sign the client certificate with the CA:
openssl rand -hex -out "$(dirname "${OPENSSL_CONF}")/serial" 5 openssl ca -extensions new_client -in csr.pem -out cert.pem
To provide a custom
subjectAltName
when signing the certificate, createcert.san
(eg:echo "email.0 = [email protected]" > cert.san
), and use the alternative form of to the above commands:openssl rand -hex -out "${OPENSSL_DIR}/serial" 5 openssl ca -extfile <( openssl-add-san new_client cert.san ) -in csr.pem -out cert.pem
As always, validate client certificate once it's generated:
openssl x509 -noout -text -in cert.pem
-
Create a certificate distribution bundle (in PFX format) which will encapsulate both client's private key and client's public certificate:
openssl pkcs12 -export -inkey key.pem -in cert.pem -out client.pfx
At this point, client's *.pem
files can be deleted.
Last but not least, we can create a certificate for our server. The approach is very similar to what we did before:
NOTE: Remember to include your custom OpenSSL configuration file:
export OPENSSL_CONF="path/to/openssl.cnf"
-
Generate server private key:
# openssl genpkey -algorithm RSA -out key.pem openssl ecparam -genkey -name secp384r1 | openssl ec -out key.pem -aes256
-
Create a Certificate Signing Request (CSR) for the server cert:
openssl req -new -key key.pem -out csr.pem openssl req -noout -text -verify -in csr.pem
-
Sign the server certificate with the CA:
openssl rand -hex -out "$(dirname "${OPENSSL_CONF}")/serial" 5 openssl ca -extensions new_server -in csr.pem -out cert.pem
You provide a custom
subjectAltName
when signing the certificate by creating a customcert.san
(eg:echo "DNS.0 = localhost" > cert.san
), then using the following alternative to the above commands:openssl rand -hex -out "$(dirname "${OPENSSL_CONF}")/serial" 5 openssl ca -extfile <( openssl-add-san new_server cert.san ) -in csr.pem -out cert.pem
As always, validate server certificate once it's generated:
openssl x509 -noout -text -in cert.pem
-
Create a certificate distribution bundle (in PFX format) which will encapsulate both client's private key and client's public certificate:
openssl pkcs12 -export -inkey key.pem -in cert.pem -out server.pfx
Any certificate can be validated against the Certificate Revocation List
(CRL) maintained by the Certificate Authority (CA) that issued that
certificate. In our case, the job is made simpler by including
crlDistributionPoints
within the generated certificates:
openssl verify -crl_check -CApath path/to/ca_dir path/to/cert.pem
Useful links:
Keeping the same private key on your root CA allows for all certificates to continue to validate successfully against the new root; all that's required of you is to trust the new root. In fact, keeping the same private key on any certificate allows you to reissue that certificate with a new set of parameters (eg: a new expiry date).
Thus, provided we have access to the original server.key.pem
, the
certificate of the server can be renewed with:
openssl req -new -key server.key.pem -out server.csr.pem
openssl ca -batch -extensions new_server -in server.csr.pem -out server.cert.pem
Currently valid certificates can be revoked manually or via the command line:
openssl ca -revoke /path/to/cert.pem
openssl ca -gencrl -out ca.crl.pem
To revoke a certificate manually (useful for when you don't have access to the actual certificate file):
- Open
index.txt
in your editor of choice- Find the line representing the certificate you wish to revoke
- Replace whatever letter the line starts with with
R
(for Revoked) - Add revocation time stamp (
yymmddhhmmssZ
) between certificate expiration timestamp and certificate serial number.
- Update the CRL:
openssl ca -gencrl -out ca.crl.pem
NOTE: Don't forget to upload the new CRL to the CRL endpoint defined in the configuration file (see
[ crl_distribution_points ]
).
Useful links:
--
Sources:
- How to Create Your Own SSL Certificate Authority for Local HTTPS Development
- OpenSSL Certificate Authority
- SSL and TLS Deployment Best Practices
- Cryptographic Best Practices
- Creating a CA
- Email in certificates
- Verify Certificate is revoked by CRL
- How can I renew my certificate authority signing key?
- Certification authority root certificate expiry and renewal