Skip to content

Instantly share code, notes, and snippets.

@Psmths
Last active November 13, 2021 07:48
Show Gist options
  • Save Psmths/589b83a90a3986e0f9caad0e2169af12 to your computer and use it in GitHub Desktop.
Save Psmths/589b83a90a3986e0f9caad0e2169af12 to your computer and use it in GitHub Desktop.
Creating a Certificate Authority with OpenSSL

Creating a Certificate Authority with OpenSSL

This tutorial will guide you step-by-step to creating a certificate authority using OpenSSL, with a three-tier structure (root, intermediate, application). All key materials are created with ECC.

Staging

The first step is to create the directory in which the CA will reside. As a simple example, we will work out of /root/ca. We will create a directory, /root/ca/db, to store the CA database flat files. Within this directory, three files must be created as follows:

touch /root/ca/db/index.txt
touch /root/ca/db/index.txt.attr 
echo 100000 > /root/ca/db/serial

Then, create a directory for the new certificates to go:

mkdir /root/ca/newcerts

Additionally, we will be creating an openssl.cnf file in the /root/ca directory as follows:

[ ca ]
default_ca = ca_conf

[ ca_conf ]
dir = /root/ca/
private_key = $dir/ca-root-private-enc.pem
certificate = $dir/ca-root-certificate.pem
new_certs_dir = $dir/newcerts

database = $dir/db/index.txt
serial = $dir/db/serial

default_md = sha512
policy = policy_strict

[ policy_strict ]
countryName = supplied
stateOrProvinceName = supplied
organizationName = supplied
organizationalUnitName  = supplied
commonName = supplied
emailAddress = optional

[ req ]
distinguished_name = req_distinguished_name
x509_extensions = ca_root
default_md = sha512

[ ca_root ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
nsComment = "LEVEL 1 ROOT CERTIFICATE"


[ ca_intermediate ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
nsComment = "LEVEL 2 INTERMEDIATE CERTIFICATE"

[ ca_user ]
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "LEVEL 3 CLIENT CERTIFICATE"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection

[ ca_server ]
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "LEVEL 3 SERVER CERTIFICATE"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

[ ca_ocsp ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
nsComment = "LEVEL 3 OCSP SERVICE CERTIFICATE"

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
stateOrProvinceName             = State or Province Name
localityName                    = Locality Name
0.organizationName              = Organization Name
organizationalUnitName          = Organizational Unit Name
commonName                      = Common Name
emailAddress                    = Email Address

countryName_default             = COUNTRY
stateOrProvinceName_default     = PROVINCE
localityName_default            = CITY
0.organizationName_default      = ORGANIZATION

Creating the Root Key

First, create the private key, in this example we are using the NIST curve secp521r1.

openssl ecparam -name secp521r1 -genkey -noout -out ca-root-private.pem

Then ensure that it is encrypted. We will be using AES-256. Afterwards, it is recommended to delete ca-root-private.pem.

openssl ec -in ca-root-private.pem -out ca-root-private-enc.pem -aes256
rm ca-root-private.pem

Finally, create the public key.

openssl ec -in ca-root-private-enc.pem -pubout -out ca-root-public.pem

With the private and public keys it is now possible to create the root certificate as follows:

openssl req -config openssl.cnf -key ca-root-private-enc.pem -new -x509 -days 7300 -sha512 -extensions ca_root -out ca-root-certificate.pem

We can validate the entered information by invoking the following command:

openssl x509 -noout -text -in ca-root-certificate.pem

As this is a root certificate, it is recommended to keep this offline at all times unless required for signing intermediate certificates. It is the source of all trust within this CA environment, and should it be compromised, all trust will be destroyed!

Creating the Intermediate Certificate

The intermediate certificate is used to sign application certificates that can be used for clients, servers, etc. Follow the same steps as the root certificate to generate the private and public keys:

openssl ecparam -name secp521r1 -genkey -noout -out ca-int-private.pem
openssl ec -in ca-int-private.pem -out ca-int-private-enc.pem -aes256
rm ca-int-private.pem
openssl ec -in ca-int-private-enc.pem -pubout -out ca-int-public.pem

Then, generate a certificate using the ca_intermediate extensions.

openssl req -config openssl.cnf -key ca-int-private-enc.pem -new -sha512 -extensions ca_intermediate -out ca-int-csr.pem

Signing the Intermediate Certificate

We must now sign the intermediate certificate. We do this by invoking:

openssl ca -config openssl.cnf -extensions ca_intermediate -days 3650 -notext -md sha512 -in ca-int-csr.pem -out ca-int-certificate.pem

We can validate the signature is OK by invoking:

openssl verify -CAfile ca-root-certificate.pem ca-int-certificate.pem

At this point, a new certificate will be placed into the newcerts directory with the first serial number, in this case, 100000. This transaction is also logged in the database.

Creating a Chain Certificate

The chain certificate will include the root and intermediate certificates in one file:

 cat ca-int-certificate.pem ca-root-certificate.pem > ca-chain-certificates.pem

Creating a Server Certificate

In order to start creating end-of-line certificates, the process is similar to creating intermediate certificates. The only difference is we point the signing entity to the intermediate certificate. To do this, in openssl.cnf, switch the privatekey and certifiacte to point to the intermediate pairs as follows:

[ ca_conf ]
dir = /root/ca/
private_key = $dir/ca-int-private-enc.pem
certificate = $dir/ca-int-certificate.pem

Then follow the same steps as before. The CN will be the subdomain for which the certificate will be deployed.

openssl ecparam -name secp521r1 -genkey -noout -out www.example.com-pri.pem
openssl ec -in www.example.com-pri.pem -pubout -out www.example.com-pub.pem
openssl req -config openssl.cnf -key www.example.com-pri.pem -new -sha512 -extensions ca_server -out www.example.com-csr.pem
openssl ca -config openssl.cnf -extensions ca_server -days 365 -notext -md sha512 -in www.example.com-csr.pem -out www.example.com-cert.pem

For Apache, you will need the following three files:

ca-chain-certificates.pem  www.example.com-cert.pem  www.example.com-pri.pem

And the corresponding Apache VHOST config would look like:

SSLEngine On
SSLCertificateFile /etc/pki/www.example.com-cert.pem
SSLCertificateKeyFile /etc/pki/www.example.com-pri.pem

For client certificate authentication (if you wish to use this), an additional set of configurations will be used:

SSLVerifyClient require
SSLVerifyDepth 10
SSLCACertificateFile /etc/pki/ca-chain-certificates.pem
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment