[UPDATED 2019.11.03]
OpenSSL can be a very frustrating piece of software, far from the plug'n play ease which modern Infuriating Technologies (IT) have got us accustomed to (with all their nicely preset defaults… and gotchas).
While netsearching immediately reveals quick and dirty howtos for personal use certificates, things get (way!) more complicated when one aims towards setting up a serious Public Key Infrastructure (PKI), even more so when doing so in parallel with the constraints - aka. Certificates Policy and Certification Practice Statement (CP/CPS) - of official Certificate Service Providers (CSP).
In order to overcome this complexity, I’ve come up with a nifty OpenSSL wrapper, which I dubbed OpenSSL-Easy, along sample OpenSSL configurations for the three most common use cases: SSL servers certificates, signature/encryption personal “IDs” and (PAM/Kerberos/Web/VPN/etc.) authentication tokens:
While intended to be easy to use interactively (see openssl-easy --help
),
OpenSSL-Easy can seamlessly be integrated into batch processes, by exporting
ad-hoc OPENSSL… environment variables (see the source code).
By default, OpenSSL-Easy does it work in the current directory (OPENSSL_ROOT='.'
).
The OpenSSL configuration file is expected to be found in this directory
(OPENSSL_CONF='./openssl.conf'
). Thus, before issuing any OpenSSL-Easy
command, one must copy one of the provided sample configuration file into
a blank directory and edit it to match one’s identity (company name, country,
city, URIs, etc.).
The (CA) private key password can be persisted accross calls thanks to the
OPENSSL_PASSWORD
(OPENSSL_PASSWORD_CA
) variable, which can conventiently
set by sourcing the OpenSSL-Easy script:
source openssl-easy set-key-passwd
Options prefixed with the plus sign (+
) are specific to OpenSSL-Easy, while
options prefixed with the minus sign (-
) will be (mostly) passed through to
OpenSSL.
All OpenSSL-Easy-generated resources (keys, certs, etc.) are identified by their ID and their creation date:
openssl-easy +id "<resource-id>" [+date "<YYYYMMDD>"] ...
Integration with PKCS#11 is readily achieved thanks to the +pkcs11-...
options
and corresponding OPENSSL_PKCS11_...
variables:
pkcs11-tool --list-token-slots
pkcs11-tool --list-objects --token-label "<token-label>"
openssl-easy +pkcs11-token "<token-label>" +pkcs11-object "<object-label>" ...
Should you need to setup your own Certification Authority (CA):
openssl-easy make-ca +name "My CA" -days 3650
On the other hand, if you plan to have your certificates signed by an external CA:
openssl-easy make-ca +id 'CA:EXTERNAL'
A server certificate is typically characterized by:
being used to authenticate its host as a server (serverAuth
)
being used to authenticate its host as a client to other servers (clientAuth
)
having a Common Name (CN) which matches its host’s fully-qualified hostname
having Subject Alternative Name(s) (SANs) which match the host’s fully-qualified hostname and potential aliases
In order to create such a certificate with OpenSSL-Easy:
Use the openssl.server.conf
configuration template
Create the private key
openssl-easy make-key +id '<hostname>'
openssl-easy show-key +id '<hostname>' # -noout -text
Create the corresponding Certificate Signing Request (CSR)
openssl-easy make-req +id '<hostname>' +name '<hostname>' \
+san-dns '<hostname>' # +san-dns ...
openssl-easy show-req +id '<hostname>' # -noout -text
Create the certificate by signing its CSR
openssl-easy make-cert +id '<hostname>'
openssl-easy show-cert +id '<hostname>' # -noout -text
OR import the certificate after having its CSR signed by an external CA
openssl-easy import-cert +id '<hostname>' -in /path/to/certificate.pem
A personal certificate is typically characterized by:
being used to digitally sign and encrypt e-mail messages (emailProtection
)
(optionally) being used to authenticate its holder as a client of servers'
services (clientAuth
)
having a Common Name (CN) which matches its holder’s full name (usually exactly as per provided identification document)
having Subject Alternative Name (SAN) which matches the holder’s fully-qualified e-mail address
In order to create such a certificate with OpenSSL-Easy:
Use the openssl.personal.conf
configuration template
Create the private key
openssl-easy make-key +id '<email>'
Create the corresponding Certificate Signing Request (CSR)
openssl-easy make-req +id '<email>' +name '<fullname>' +email <email>
Create the certificate by signing its CSR
openssl-easy make-cert +id '<email>'
OR import the certificate after having its CSR signed by an external CA
openssl-easy import-cert +id '<email>' -in /path/to/certificate.pem
An authentication certificate is typically characterized by:
being used to authenticate its holder as a client of servers' services
(clientAuth
)
having a Common Name (CN) - or some other part of its Distinguished Name (DN) or its Subject Alternative Name (SAN) - which matches its holder’s username
having extensions that allows inter-operability with authentication services such as, e.g. MIT Kerberos V
In order to create such a certificate with OpenSSL-Easy:
Use the openssl.authentication.conf
configuration template
Define your Kerberos realm
export KERBEROS_REALM='EXAMPLE.ORG'
Create the private key
openssl-easy make-key +id '<principal>'
Create the corresponding Certificate Signing Request (CSR)
openssl-easy make-req +id '<principal>' +name '<username>'
Create the certificate by signing its CSR; WARNING: make sure to specify
the <username>
again (required by MIT Kerberos V extensions)
openssl-easy make-cert +id '<principal>' +name '<username>'
You’ll also have to create the certificates for hosts providing/using authentication services:
for MIT Kerberos V Key Distribution Center (KDC) server, after creating the ad-hoc private key and CSR
openssl-easy make-cert +id '<hostname>' -extensions 'CA_KDC_Server_Certificate_Extensions'
for an OpenVPN server, after creating the ad-hoc private key and CSR
openssl-easy make-cert +id '<hostname>' -extensions 'CA_VPN_Server_Certificate_Extensions'
for an OpenVPN (host) client, after creating the ad-hoc private key and CSR
openssl-easy make-cert +id '<hostname>' -extensions 'CA_VPN_Client_Certificate_Extensions'
To check a CA-signed certificate’s status against its published/embedded CRL Distribution Point:
openssl-easy verify-crl +id ...
To check a CA-signed certificate’s status against its published/embedded OCSP Responder:
openssl-easy verify-oscp +id ... # -resp_text
To find out about valid certificates (at a given date):
openssl-easy list-certs # +date ...
To find out about expired certificates (at a given date):
openssl-easy list-certs +expired # +date ...
To find out about revoked certificates (at a given date):
openssl-easy list-certs +revoked # +date ...
The format of the generated Tab-Separated Values (TSV) being:
ID DATE SUBJECT SERIAL STATUS DATE FILE
For large deployments, it is recommended setting up a Root Certification Authority (CA), which shall be used - solely! - to sign Intermediate/Issuing Certification Authorities (ICAs) certificates for each purpose (such as the one we covered above).
First, get hold of the ad-hoc OpenSSL configuration file:
Then:
Start by creating the root CA:
source openssl-easy set-key-passwd # CA password
openssl-easy make-ca +name 'My Root CA' -days 3650
Then, for each ICA, create its CA-signed certificate:
source openssl-easy set-key-passwd # ICA password
openssl-easy make-key +id 'ICA-<Purpose>'
openssl-easy make-req +id 'ICA-<Purpose>' +name 'My <Purpose> ICA'
openssl-easy make-cert +id 'ICA-<Purpose>'
Copy the ICA private key and certificate into its own directory:
mkdir -p /path/to/'ICA-<Purpose>'/CA
cat keys/'ICA-<Purpose>'-key-*.pem > /path/to/'ICA-<Purpose>'/CA/key.pem
cat certs/'ICA-<Purpose>'-cert-*.pem > /path/to/'ICA-<Purpose>'/CA/cert.pem
cat certs/'ICA-<Purpose>'-cert-*.pem > /path/to/'ICA-<Purpose>'/CA/chain.pem
Switch to the ICA directory and initialize it:
cd /path/to/'ICA-<Purpose>'
openssl-easy make-ca +id 'CA:INTERMEDIATE'
Create ICA-signed end-usage certificates
One of the common pitfalls that comes with generating your own certificates
are their declared (and CA-certified) usage purpose, known as their Key Usage
(KU)
and Extended Key Usage (EKU)
, as standardized by
RFC 5280
The sample OpenSSL configurations linked above take care of declaring the appropriate (Extended) Key Usage for each use case.
One should note the difference between the keyEncipherment
and keyAgreement
key usages:
keyEncipherment
: the (symetric) encryption key for the (VPN/web/…) TLS
session is encrypted using the peers (asymetric) public/private keys;
this means this encryption key can be recovered by anyone who gets hold
the private key
keyAgreement
: the (symetric) encryption key for the (VPN/web/…) TLS
session is derived using the Diffie-Hellman protocol (or equivalent);
this means the encryption key can NEVER be recovered (even by someone
who would get hold of the peer private key) and guarantees Forward
Secrecy