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 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:
cert/: This directory contains the certificates generated and signed
by the CA. For the root CA, this includes the root-CA certificate, itself. For the
intermediate certificate, this includes the intermediate-CA certificate and any server or
client certificates signed by the intermediate CA.cert-new/: This directory stores a copy of each certificate signed by
the CA, with the certificate's serial number as the filename. It helps maintain a backup of
all issued certificates.crl/: This directory contains the certificate-revocation lists (CRL)
generated by the CA. A CRL is a list of certificates that have been revoked by the CA prior
to their expiration date.key/: This directory contains the private keys for the CA, including
the root-CA and intermediate-CA private keys. These keys are used to sign certificates and
CRLs. The private keys should be kept secure and not shared; in the case of the root CA,
the private keys should be kept offline, preferably in a hardware security module.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
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
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
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.
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.
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!
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 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.
openssl x509 -noout -text -in ca-root/cert/ca-root.pem
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.
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 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 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
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.
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 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
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.
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.
openssl rsa -noout -text -in <server key name>.pem
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
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 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
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.