OpenSSL - Self-signed Certificate Chain

This documentation contains the complete set of commands to create a new OpenSSL self-signed certificate chain with V3 subjectAltName (SAN) extensions enabled. SANs can be included in a certificate by adding each domain as a comma-delimited string.

Each key can be encrypted or unencrypted, with multiple encryption options; AES (aes128 or aes256) is recommended.

Optional verification can also be performed between multiple levels of certificates to ensure the chain of trust is valid.

To keep it simple, this documentation will use specific paths for directories and files; change paths wherever necessary for your configuration.

Following best security practice, both a root certificate authority (CA) and intermediate certificate authority will be created.

Create Directory Structure

Create directories for storing root-CA files:

mkdir -p ca-root/{cert/,cert-new/,crl/,csr/,key/}

Create directories for storing intermediate-CA files:

mkdir -p ca-intermediate/{cert/,cert-new/,crl/,csr/,key/}

The directory structure is as follows:

Create Serial File

A serial file is used to keep track of the latest serial number that was used to issue a certificate. Multiple certificates should never be issued with the same serial number from the same CA. The value of the file should be hexadecimal, and it must contain at least 2 digits. By setting the initial value to 1000, it is certain that the serial numbers begin at 1000 and increment for each subsequent certificate issued.

echo "1000" | tee ca-root/serial ca-intermediate/serial

Create CRL-number File

crlnumber is an OpenSSL configuration directive specifying the current CRL number. The CRL number is a unique integer which is incremented each time a new CRL is generated. This helps track the latest CRL issued by the CA and ensures that CRLs are issued, sequentially.

echo "0100" | tee ca-root/crlnumber ca-intermediate/crlnumber

Create Index File

OpenSSL requires a database in the form of a an index file to keeps track of the certificates that have been issued by the CA. Each line in the file represents a certificate and contains information such as the certificate's status, the certificate's expiration date, the certificate's serial number, and the certificate subject's distinguished name (DN).

As no certificates have been issued at this point, and OpenSSL requires that the file exists, create the index as an empty file.

touch ca-root/index.txt ca-intermediate/index.txt

Create Root-CA Configuration File

A configuration file for the root CA must be created in order to use specific targets and directives for the root CA. The following example will use ca-root.cnf:

[ ca ]							 # The default CA section
default_ca = CA_default                                  # The default CA name

[ CA_default ]                                           # Default settings for the CA
dir               = ca-root                              # CA directory
certs             = $dir/cert                            # Certificates directory
crl_dir           = $dir/crl                             # CRL directory
new_certs_dir     = $dir/cert-new                        # New certificates directory
database          = $dir/index.txt                       # Certificate index file
serial            = $dir/serial                          # Serial number file
RANDFILE          = $dir/key/.rand                       # Random number file
private_key       = $dir/key/ca-root.pem                 # Root CA private key
certificate       = $dir/cert/ca-root.pem                # Root CA certificate
crl               = $dir/crl/ca-root.pem                 # Root CA CRL
crlnumber         = $dir/crlnumber                       # Root CA CRL number
crl_extensions    = crl_ext                              # CRL extensions
default_crl_days  = 30                                   # Default CRL validity days
default_md        = sha256                               # Default message digest
preserve          = no                                   # Preserve existing extensions
email_in_dn       = no                                   # Exclude email from the DN
name_opt          = ca_default                           # Formatting options for names
cert_opt          = ca_default                           # Certificate output options
policy            = policy_strict                        # Certificate policy
unique_subject    = no                                   # Allow multiple certs with the same DN

[ policy_strict ]                                        # Policy for stricter validation
countryName             = match                          # Must match the issuer's country
stateOrProvinceName     = match                          # Must match the issuer's state
organizationName        = match                          # Must match the issuer's organization
organizationalUnitName  = optional                       # Organizational unit is optional
commonName              = supplied                       # Must provide a common name
emailAddress            = optional                       # Email address is optional

[ req ]                                                  # Request settings
default_bits        = 2048                               # Default key size
distinguished_name  = req_distinguished_name             # Default DN template
string_mask         = utf8only                           # UTF-8 encoding
default_md          = sha256                             # Default message digest
prompt              = no                                 # Non-interactive mode

[ req_distinguished_name ]                               # Template for the DN in the CSR
countryName                     = Country Name (2 letter code)
stateOrProvinceName             = State or Province Name (full name)
localityName                    = Locality Name (city)
0.organizationName              = Organization Name (company)
organizationalUnitName          = Organizational Unit Name (section)
commonName                      = Common Name (your domain)
emailAddress                    = Email Address

[ v3_ca ]                                                # Root CA certificate extensions
subjectKeyIdentifier = hash                              # Subject key identifier
authorityKeyIdentifier = keyid:always,issuer             # Authority key identifier
basicConstraints = critical, CA:true                     # Basic constraints for a CA
keyUsage = critical, keyCertSign, cRLSign                # Key usage for a CA

[ crl_ext ]                                              # CRL extensions
authorityKeyIdentifier = keyid:always,issuer             # Authority key identifier

Modify the values to suit your configuration.

Create Intermediate-CA Configuration File

A configuration file for the intermediate CA must be created in order to use specific targets and directives for the intermediate CA. The following example will use ca-intermediate.cnf:

[ ca ]                                                             # The default CA section
default_ca = CA_default                                            # The default CA name

[ CA_default ]                                                     # Default settings for the intermediate CA
dir               = ca-intermediate                                # Intermediate CA directory
certs             = $dir/cert                                      # Certificates directory
crl_dir           = $dir/crl                                       # CRL directory
new_certs_dir     = $dir/cert-new                                  # New certificates directory
database          = $dir/index.txt					               # Certificate index file
serial            = $dir/serial                                    # Serial number file
RANDFILE          = $dir/key/.rand                                 # Random number file
private_key       = $dir/key/ca-intermediate.pem                   # Intermediate CA private key
certificate       = $dir/cert/ca-intermediate.pem                  # Intermediate CA certificate
crl               = $dir/crl/ca-intermediate.pem                   # Intermediate CA CRL
crlnumber         = $dir/crlnumber                                 # Intermediate CA CRL number
crl_extensions    = crl_ext                                        # CRL extensions
default_crl_days  = 30                                             # Default CRL validity days
default_md        = sha256                                         # Default message digest
preserve          = no                                             # Preserve existing extensions
email_in_dn       = no                                             # Exclude email from the DN
name_opt          = ca_default                                     # Formatting options for names
cert_opt          = ca_default                                     # Certificate output options
policy            = policy_loose                                   # Certificate policy

[ policy_loose ]                                                   # Policy for less strict validation
countryName             = optional                                 # Country is optional
stateOrProvinceName     = optional                                 # State or province is optional
localityName            = optional                                 # Locality is optional
organizationName        = optional                                 # Organization is optional
organizationalUnitName  = optional                                 # Organizational unit is optional
commonName              = supplied                                 # Must provide a common name
emailAddress            = optional                                 # Email address is optional

[ req ]                                                            # Request settings
default_bits        = 2048                                         # Default key size
distinguished_name  = req_distinguished_name                       # Default DN template
string_mask         = utf8only                                     # UTF-8 encoding
default_md          = sha256                                       # Default message digest
x509_extensions     = v3_intermediate_ca                           # Extensions for intermediate CA certificate

[ req_distinguished_name ]                                         # Template for the DN in the CSR
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

[ v3_intermediate_ca ]                                             # Intermediate CA certificate extensions
subjectKeyIdentifier = hash                                        # Subject key identifier
authorityKeyIdentifier = keyid:always,issuer                       # Authority key identifier
basicConstraints = critical, CA:true, pathlen:0                    # Basic constraints for a CA
keyUsage = critical, digitalSignature, cRLSign, keyCertSign        # Key usage for a CA

[ crl_ext ]                                                        # CRL extensions
authorityKeyIdentifier=keyid:always                                # Authority key identifier

[ server_cert ]                                                    # Server certificate extensions
basicConstraints = CA:FALSE                                        # Not a CA certificate
nsCertType = server                                                # Server certificate type
keyUsage = critical, digitalSignature, keyEncipherment             # Key usage for a server cert
extendedKeyUsage = serverAuth                                      # Extended key usage for server authentication purposes (e.g., TLS/SSL servers).
authorityKeyIdentifier = keyid,issuer                              # Authority key identifier linking the certificate to the issuer's public key.

Modify the values to suit your configuration.

Create Root-CA Key

The root-CA key should be strong due to being the root of all certificates signed by it; the following example uses 4096-bit RSA for the root-CA key:

openssl genrsa -out ca-root/key/ca-root.pem 4096

Set secure permisisons for the root-CA key, so only root can read it; the following example also protects against unintentional modification of the root-CA key:

chmod 400 ca-root/key/ca-root.pem

The root-CA key must not be compromised! If the root-CA key is compromised, all certificates signed by it, and certificates signed by those certificates, can no longer be trusted!

Verify Root-CA Key

Optionally, you may verify the root-CA key via the following command:

openssl rsa -noout -text -in ca-root/key/ca-root.pem

It is recommended that you perform this step to ensure that the root-CA key is valid.

Create Root-CA Certificate

Create the root-CA certificate via the following command:

openssl req -config ca-root.cnf -key ca-root/key/ca-root.pem -new -x509 -days 7300 -sha256 -extensions v3_ca -out ca-root/cert/ca-root.pem -subj "/C=Country/ST=State/L=Locality/O=Organisation/OU=Organisational Unit/CN=Common Name"

Give the root-CA certificate a long validity timeframe as it will be used only to sign intermediate CAs, not directly sign server/client CAs.

Verify Root-CA Certificate

openssl x509 -noout -text -in ca-root/cert/ca-root.pem

Create Intermediate-CA Key

The intermediate-CA key is not required to be as strong as the root-CA key; the following example uses 3072-bit RSA for the intermediate-CA key:

openssl genrsa -out ca-intermediate/key/ca-intermediate.pem 3072

Set secure permisisons for the intermediate-CA key, so only root can read it; the following example also protects against unintentional modification of the intermediate-CA key:

chmod 400 ca-intermediate/key/ca-intermediate.pem

While not as catastrophic as the root-CA key being compromised, compromise of the intermediate-CA key should be avoided. Should the intermediate CA be compromised, all server/client certificates signed by it can no longer be trusted.

Verify Intermediate-CA Key

Optionally, you may verify the intermediate-CA key via the following command:

openssl rsa -noout -text -in ca-intermediate/key/ca-intermediate.pem

It is recommended that you perform this step to ensure that the intermediate-CA key is valid.

Create Intermediate-CA Certificate-Signing Request

Create an intermediate-CA CSR via the following command:

openssl req -config ca-intermediate.cnf -key ca-intermediate/key/ca-intermediate.pem -new -sha256 -out ca-intermediate/cert/ca-intermediate-csr.pem -subj "/C=Country/ST=State/L=Locality/O=Organisation/OU=Organisational Unit/CN=Common Name"

Create Intermediate-CA Certificate

Create an intermediate-CA certificate via the following command:

openssl ca -config ca-root.cnf -extensions v3_intermediate_ca -days 1875 -notext -md sha256 -in ca-intermediate/cert/ca-intermediate-csr.pem -out ca-intermediate/cert/ca-intermediate.pem

The intermediate-CA certificate should have been added to the index; you may check this via the following command:

cat ca-root/index.txt

Verify Intermediate-CA Certificate

Optionally, you may verify the intermediate-CA certificate via the following command:

openssl x509 -noout -text -in ca-intermediate/cert/ca-intermediate.pem

It is recommended that you perform this step to ensure that the intermediate-CA certificate is valid.

Verify Chain-of-Trust (Root CA to Intermediate CA)

Optionally, you may verify the chain-of-trust from the root CA to the intermediate CA via the following command:

openssl verify -CAfile ca-root/cert/ca-root.pem ca-intermediate/cert/ca-intermediate.pem

It is recommended that you perform this step to ensure that the chain-of-trust is valid.

Create Certificate Bundle

Create a certificate bundle by combining the root CA and intermediate CA into a single file via the following command:

cat ca-intermediate/cert/ca-intermediate.pem ca-root/cert/ca-root.pem > ca-intermediate/cert/ca-chain.pem

Verify Chain-of-Trust (Certificate Bundle)

Optionally, you may verify the chain-of-trust for the certificate bundle via the following command:

openssl verify -CAfile ca-intermediate/cert/ca-chain.cert.pem ca-intermediate/cert/ca-intermediate.pem

It is recommended that you perform this step to ensure that the chain-of-trust is valid.

Create Server Key

The server key is not required to be as strong as the intermediate-CA key; the following example uses RSA for the server key:

openssl genpkey -algorithm RSA -out ca-intermediate/key/server.pem

Set secure permisisons for the server key, so only root can read it; the following example also protects against unintentional modification of the server key:

chmod 400 ca-intermediate/key/server.pem

Compromise of the server key should be avoided. Should the server key be compromised, the single certificate signed by it can no longer be trusted.

Verify Server Key

openssl rsa -noout -text -in <server key name>.pem

Create Server Certificate-Signing Request

Create a server CSR via the following command:

openssl req -config ca-intermediate.cnf -key ca-intermediate/key/server.pem -new -sha256 -out ca-intermediate/csr/server.pem

Create Server-Certificate-Extensions Configuration File

Add server-certificate extensions necessary for SANs by adding the following to a configuration file; in this example, server-ext.cnf:

basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = 0.example.com
DNS.2 = 1.example.com
DNS.3 = 2.example.com
DNS.4 = 3.example.com

Create Server Certificate

Create a server certificate via the following command:

openssl x509 -req -in ca-intermediate/csr/server.pem -CA ca-intermediate/cert/ca-chain.pem -CAkey ca-intermediate/key/intermediate.pem -out ca-intermediate/cert/server.pem -CAcreateserial -days 90 -sha256 -extfile server-ext.cnf

Verify Server Certificate

Optionally, you may verify the server certificate via the following command:

openssl x509 -noout -text -in ca-intermediate/cert/server.pem

It is recommended that you perform this step to ensure that the server certificate is valid.

Sitemap