OpenSSL create Certificate Chain [Root & Intermediate CA]


OpenSSL

Openssl create certificate chain requires Root CA and Intermediate certificate, In this article I will share Step-by-Step Guide to create root and intermediate certificates and then use these certificates to create certificate CA bundle in Linux. I hope you have an overview of all the terminologies used with OpenSSL.

 

Root vs Intermediate Certificate

  • A certificate chain or certificate CA bundle is a sequence of certificates, where each certificate in the chain is signed by the subsequent certificate.
  • The Root CA is the top level of certificate chain while intermediate CAs or Sub CAs are Certificate Authorities that issue off an intermediate root.
  • Typically, the root CA does not sign server or client certificates directly.
  • The root CA is only ever used to create one or more intermediate CAs, which are trusted by the root CA to sign certificates on their behalf. This is best practice.
  • It allows the root key to be kept offline and unused as much as possible, as any compromise of the root key is disastrous.
  • An intermediate certificate authority (CA) is an entity that can sign certificates on behalf of the root CA.
  • The root CA signs the intermediate certificate, forming a chain of trust.
  • The purpose of using an intermediate CA is primarily for security.
  • The root key can be kept offline and used as infrequently as possible.
  • If the intermediate key is compromised, the root CA can revoke the intermediate certificate and create a new intermediate cryptographic pair.
OpenSSL create certificate chain with Root & Intermediate CA

 

Pre-requisites: Install OpenSSL

On RHEL/CentOS 7/8 you can use yum or dnf command respectively while on Ubuntu use apt command to install openssl rpm

NOTE:
On RHEL system you must have an active subscription to RHN or you can configure a local offline repository using which "yum" package manager can install the provided rpm and it's dependencies.
yum -y install openssl

 

OpenSSL encrypted data with salted password (Optional)

When we create private key for Root CA certificate, we have an option to either use encryption for private key or create key without any encryption. As if we choose to create private key with encryption such as 3DES, AES then you will have to provide a passphrase every time you try to access the private key.

I have already written another article with the steps to generate self-signed certificate, with and without salted password.

In this article we we will use RSA key which will not encrypt the private key, but you can decide to use AES or 3DES as your preferred algorithm.

 

Step 1: Create OpenSSL Root CA directory structure

We can also create CA bundle with all the certificates without creating any directory structure and using some manual tweaks but let us follow the long procedure to better understanding. In RHEL/CentOS 7/8 the default location for all the certificates are under /etc/pki/tls. But for this article we will create a new directory structure /root/myCA/ to store our certificates.

Create a directory structure to store the CA files, certificates, and private keys:

mkdir -p ~/myCA/rootCA/{certs,crl,newcerts,private,csr}
mkdir -p ~/myCA/intermediateCA/{certs,crl,newcerts,private,csr}

Each directory in your Certificate Authority (CA) folder structure serves a specific purpose:

  • certs: 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 CA, this includes the intermediate CA certificate and any server or client certificates signed by the intermediate CA.
  • crl: The Certificate Revocation List (CRL) directory contains the CRLs generated by the CA. A CRL is a list of certificates that have been revoked by the CA before their expiration date.
  • newcerts: This directory stores a copy of each certificate signed by the CA, with the certificate's serial number as the file name. It helps maintain a backup of all issued certificates.
  • private: 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.
IMPORTANT NOTE:
The majority of the files that the CA uses are visible to anyone on the system or at least to anyone who makes any use of the certificates issued by our CA. The one notable exception is the CA certificate’s private key. The private key should never be disclosed to anyone not authorized to issue a certificate or CRL from our CA. The private key should be stored in hardware, or at least on a machine that is never put on a network

A serial file is used to keep track of the last serial number that was used to issue a certificate. It’s important that no two certificates ever be issued with the same serial number from the same CA. OpenSSL is somewhat quirky about how it handles this file. It expects the value to be in hex, and it must contain at least two digits. By setting the initial value to 1000, we ensure that the serial numbers start from 1000 and increment for each subsequent certificate issued.

echo 1000 > ~/myCA/rootCA/serial
echo 1000 > ~/myCA/intermediateCA/serial

A crlnumber is a configuration directive specifying the file that contains the current CRL number. The CRL number is a unique integer that is incremented each time a new Certificate Revocation List (CRL) is generated. This helps in tracking the latest CRL issued by the CA and ensuring that CRLs are issued in a proper sequence. We have given a random digit in our crlnumber file which will be used to keep track of all certs which are revocated.

echo 0100 > ~/myCA/rootCA/crlnumber 
echo 0100 > ~/myCA/intermediateCA/crlnumber

Next we will create index.txt file which is a database of sorts that keeps track of the certificates that have been issued by the CA. Each line in the index.txt file represents a certificate and contains information such as the certificate's status (e.g., valid, revoked), the certificate's expiration date, the certificate's serial number, and the certificate subject's distinguished name (DN).

Since no certificates have been issued at this point and OpenSSL requires that the file exist, we’ll simply create an empty file.

touch ~/myCA/rootCA/index.txt
touch ~/myCA/intermediateCA/index.txt

Check the list of contents under /root/myCA

├── intermediateCA
│   ├── certs
│   ├── crl
│   ├── csr
│   ├── crlnumber
│   ├── index.txt
│   ├── newcerts
│   ├── private
│   └── serial
└── rootCA
    ├── certs
    ├── crl
    ├── csr
    ├── crlnumber
    ├── index.txt
    ├── newcerts
    ├── private
    └── serial

10 directories, 6 files

 

Step 2: Configure openssl.cnf for Root and Intermediate CA Certificate

We will create two separate openssl.cnf file (each for root and intermediate CA).

Here is our openssl_root.cnf file:

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

[ CA_default ]                                           # Default settings for the CA
dir               = /root/myCA/rootCA                    # CA directory
certs             = $dir/certs                           # Certificates directory
crl_dir           = $dir/crl                             # CRL directory
new_certs_dir     = $dir/newcerts                        # New certificates directory
database          = $dir/index.txt                       # Certificate index file
serial            = $dir/serial                          # Serial number file
RANDFILE          = $dir/private/.rand                   # Random number file
private_key       = $dir/private/ca.key.pem              # Root CA private key
certificate       = $dir/certs/ca.cert.pem               # Root CA certificate
crl               = $dir/crl/ca.crl.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

[ v3_intermediate_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

Here is our openssl_intermediate.cnf file:

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

[ CA_default ]                                           # Default settings for the intermediate CA
dir               = /root/myCA/intermediateCA            # Intermediate CA directory
certs             = $dir/certs                           # Certificates directory
crl_dir           = $dir/crl                             # CRL directory
new_certs_dir     = $dir/newcerts                        # New certificates directory
database          = $dir/index.txt                       # Certificate index file
serial            = $dir/serial                          # Serial number file
RANDFILE          = $dir/private/.rand                   # Random number file
private_key       = $dir/private/intermediate.key.pem    # Intermediate CA private key
certificate       = $dir/certs/intermediate.cert.pem     # Intermediate CA certificate
crl               = $dir/crl/intermediate.crl.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.
NOTE:
If you are planning to add more intermediate CA certificate then I would recommend to give either a higher value for pathlen or just remove it in the following section. As pathlen restricts creating any further intermediate CA in the chain. You may also create another section v3_intermediate_ca_n and there you can add pathlen: 0 to make sure that is the last intermediate certificate in your chain.

 

Step 3: Generate the root CA Certificate

You can also refer How to create Self-Signed CA Certificate with and without maintaining a database where I have covered detailed steps to generate your own CA certificate.

Create an RSA key pair for the root CA without a password:

openssl genrsa -out ~/myCA/rootCA/private/ca.key.pem 4096
chmod 400 ~/myCA/rootCA/private/ca.key.pem
IMPORTANT NOTE:
If this key is compromised, the integrity of your CA is compromised, which essentially means that any certificates issued, whether they were issued before the key was compromised or after, can no longer be trusted.

We will use openssl command to view the content of private key:

openssl rsa -noout -text -in ~/myCA/rootCA/private/ca.key.pem

Create the root CA certificate:

  • OpenSSL create certificate chain requires Root and Intermediate Certificate. In this step you'll take the place of VeriSign, Thawte, etc.
  • Use the Root CA key cakey.pem to create a Root CA certificate cacert.pem
  • Give the root certificate a long expiry date. Once the root certificate expires, all certificates signed by the CA become invalid.
  • Whenever you use the openssl req tool, you must specify a configuration file to use with the -config option, otherwise OpenSSL will default to /etc/pki/tls/openssl.cnf
  • We will use v3_ca extensions to create CA certificate
IMPORTANT NOTE:
The Common Name (CN) of the CA and the Server certificates must NOT match or else a naming collision will occur and you'll get errors later on.

Use below command to create Root Certificate Authority Certificate cacert.pem. I have specified the Subj inline to the same command, you can update the command based on your environment.

openssl req -config openssl_root.cnf -key ~/myCA/rootCA/private/ca.key.pem -new -x509 -days 7300 -sha256 -extensions v3_ca -out ~/myCA/rootCA/certs/ca.cert.pem -subj "/C=US/ST=California/L=San Francisco/O=Example Corp/OU=IT Department/CN=Root CA"

The CA certificate can be world readable so that it can be used to sign the cert by anyone.

chmod 444 ~/myCA/rootCA/certs/ca.cert.pem

Execute the below command for openssl verify root CA certificate

openssl x509 -noout -text -in ~/myCA/rootCA/certs/ca.cert.pem

Sample Output:

OpenSSL create Certificate Chain [Root & Intermediate CA]

The output shows:

  • the Signature Algorithm used
  • the dates of certificate Validity
  • the Public-Key bit length
  • the Issuer, which is the entity that signed the certificate
  • the Subject, which refers to the certificate itself
NOTE:
The Issuer and Subject are identical as the certificate is self-signed.

The output also shows the X509v3 extensions. We applied the v3_ca extension, so the options from [ v3_ca ] should be reflected in the output.

OpenSSL create Certificate Chain [Root & Intermediate CA]

 

Step 4: Generate the intermediate CA key pair and certificate

Create an RSA key pair for the intermediate CA without a password and secure the file by removing permissions to groups and others:

openssl genrsa -out ~/myCA/intermediateCA/private/intermediate.key.pem 4096
chmod 400 ~/myCA/intermediateCA/private/intermediate.key.pem

Create the intermediate CA certificate signing request (CSR). If you are not familiar with the content to be provided with CSR then you should read Things to consider when creating CSR with OpenSSL

openssl req -config openssl_intermediate.cnf -key ~/myCA/intermediateCA/private/intermediate.key.pem -new -sha256 -out ~/myCA/intermediateCA/certs/intermediate.csr.pem -subj "/C=US/ST=California/L=San Francisco/O=Example Corp/OU=IT Department/CN=Intermediate CA"

Sign the intermediate CSR with the root CA key:

openssl ca -config openssl_root.cnf -extensions v3_intermediate_ca -days 3650 -notext -md sha256 -in ~/myCA/intermediateCA/certs/intermediate.csr.pem -out ~/myCA/intermediateCA/certs/intermediate.cert.pem

If you notice, I am using openssl ca command to sign the certificate, but do you know we could also use openssl x509 command? Learn the difference between openssl ca and openssl x509, which one to use when?

Sample Output:

OpenSSL create Certificate Chain [Root & Intermediate CA]

Assign 444 permission to the CRT to make it readable by everyone:

chmod 444 ~/myCA/intermediateCA/certs/intermediate.cert.pem

The index.txt file is where the OpenSSL ca tool stores the certificate database. Do not delete or edit this file by hand. It should now contain a line that refers to the intermediate certificate.

# cat ~/myCA/rootCA/index.txt
V 330503082700Z 1000 unknown /C=US/ST=California/O=Example Corp/OU=IT Department/CN=Intermediate CA

Here,

  • V: This field indicates the status of the certificate. In this case, V means "Valid." Other possible values are R for "Revoked" and E for "Expired."
  • 330503082700Z: This field represents the expiration date of the certificate in the format YYMMDDHHMMSSZ. In this example, the certificate will expire on 30th May 2033 at 08:27:00 UTC.
  • 1000: This field is the certificate serial number in hexadecimal format.
  • unknown: This field shows the revocation reason if the certificate has been revoked. For valid certificates, this field usually contains the value "unknown" or is left empty.
  • /C=US/ST=California/O=Example Corp/OU=IT Department/CN=Intermediate CA: This field contains the subject's distinguished name (DN) in the certificate, which consists of various components such as country (C), state or province (ST), organization (O), organizational unit (OU), and common name (CN). In this example, the DN components are as follows:
    • C: US
    • ST: California
    • O: Example Corp
    • OU: IT Department
    • CN: Intermediate CA

Verify the Intermediate CA Certificate content

openssl x509 -noout -text -in ~/myCA/intermediateCA/certs/intermediate.cert.pem

Sample Output:

OpenSSL create Certificate Chain [Root & Intermediate CA]

Next openssl verify intermediate certificate against the root certificate. An OK indicates that the chain of trust is intact.

openssl verify -CAfile ~/myCA/rootCA/certs/ca.cert.pem ~/myCA/intermediateCA/certs/intermediate.cert.pem

Output:

/root/myCA/intermediateCA/certs/intermediate.cert.pem: OK

 

Step 5: Generate OpenSSL Create Certificate Chain (Certificate Bundle)

To openssl create certificate chain (certificate bundle), concatenate the intermediate and root certificates together.

In the below example I have combined my Root and Intermediate CA certificates to openssl create certificate chain in Linux. We will use this file later to verify certificates signed by the intermediate CA.

cat ~/myCA/intermediateCA/certs/intermediate.cert.pem ~/myCA/rootCA/certs/ca.cert.pem > ~/myCA/intermediateCA/certs/ca-chain.cert.pem

After openssl create certificate chain, to verify certificate chain use below command:

openssl verify -CAfile ~/myCA/intermediateCA/certs/ca-chain.cert.pem ~/myCA/intermediateCA/certs/intermediate.cert.pem

Output:

/root/myCA/intermediateCA/certs/intermediate.cert.pem: OK

 

Step 6: Generate and sign server certificate using Intermediate CA

Create a private key for the server:

openssl genpkey -algorithm RSA -out ~/myCA/intermediateCA/private/www.example.com.key.pem
chmod 400 ~/myCA/intermediateCA/private/www.example.com.key.pem

Create a certificate signing request (CSR) for the server:

openssl req -config ~/myCA/openssl_intermediate.cnf -key ~/myCA/intermediateCA/private/www.example.com.key.pem -new -sha256 -out ~/myCA/intermediateCA/csr/www.example.com.csr.pem

You'll be asked a series of questions about the certificate. For the Common Name question, you should enter the domain name of the server (e.g., www.example.com).

OR

You can automate the certificate signing request (CSR) creation by supplying default answers to the questions asked by the openssl req command.

These defaults can be specified in the openssl.cnf (or openssl_intermediate.cnf in this case) file, under the [ req_distinguished_name ] section.

[ req_distinguished_name ]
countryName_default = US
stateOrProvinceName_default = California
localityName_default = San Francisco
0.organizationName_default = Example Corp
organizationalUnitName_default = IT Department
commonName_default = www.example.com
emailAddress_default = admin@example.com

I have not added any SAN Field extensions, but you can also choose to add additional SAN Fields to create a SAN Certificate.

By including these lines in your openssl_intermediate.cnf file, openssl req will use these as the default values for the corresponding fields, allowing the command to be run non-interactively.

Then, you can use the -batch option with openssl req command to automatically use these defaults without prompting for them:

openssl req -config ~/myCA/openssl_intermediate.cnf -key ~/myCA/intermediateCA/private/www.example.com.key.pem -new -sha256 -out ~/myCA/intermediateCA/csr/www.example.com.csr.pem -batch

Sign the server CSR with the intermediate CA:

openssl ca -config ~/myCA/openssl_intermediate.cnf -extensions server_cert -days 375 -notext -md sha256 -in ~/myCA/intermediateCA/csr/www.example.com.csr.pem -out ~/myCA/intermediateCA/certs/www.example.com.cert.pem

Sample Output:

Using configuration from /root/myCA/openssl_intermediate.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 4096 (0x1000)
        Validity
            Not Before: Jun 12 05:32:08 2023 GMT
            Not After : Jun 21 05:32:08 2024 GMT
        Subject:
            countryName               = US
            stateOrProvinceName       = California
            localityName              = San Francisco
            organizationName          = Example Corp
            organizationalUnitName    = IT Department
            commonName                = www.example.com
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Cert Type: 
                SSL Server
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication
            X509v3 Authority Key Identifier: 
                A1:99:57:5D:B1:10:A2:24:C8:FE:09:D4:48:24:B7:0F:2B:C5:D2:2B
Certificate is to be certified until Jun 21 05:32:08 2024 GMT (375 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Verify the server certificate:

openssl x509 -noout -text -in ~/myCA/intermediateCA/certs/www.example.com.cert.pem

Sample Output:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 4096 (0x1000)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, ST = California, O = Example Corp, OU = IT Department, CN = Intermediate CA
        Validity
            Not Before: Jun 12 05:32:08 2023 GMT
            Not After : Jun 21 05:32:08 2024 GMT
        Subject: C = US, ST = California, L = San Francisco, O = Example Corp, OU = IT Department, CN = www.example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:8f:1b:b4:59:15:07:5b:ba:89:97:cc:98:bf:53:
                    18:e9:e7:3a:a1:cd:e3:74:75:cd:1e:6d:02:7b:4f:
                    66:89:32:d0:ea:35:19:bc:d4:66:6a:71:e1:a2:66:
                    a4:01:24:85:0a:c6:c8:17:3a:e8:63:12:bd:ef:78:
                    cf:f4:e6:3c:67:1c:41:d5:12:d9:d7:3b:fb:3a:80:
                    7a:35:f6:0d:ee:33:c4:4a:3a:91:a9:e4:33:e0:63:
                    97:97:0e:06:ca:71:34:16:bc:29:93:83:cf:01:bf:
                    da:29:b5:e2:48:01:3a:96:b6:1a:3b:9f:70:80:67:
                    bd:7f:14:e4:bf:25:f7:0f:b2:1a:03:37:7f:f1:93:
                    7f:3d:08:6f:1e:ed:d7:13:50:f9:a3:e6:17:5e:7f:
                    8f:c4:8e:0e:a5:ce:d0:23:0d:bc:6a:85:d2:e5:28:
                    bf:0a:84:53:93:a0:6d:d6:07:25:36:6b:df:c9:ba:
                    dd:16:52:6a:7f:11:fd:5f:1d:ca:e0:50:87:19:00:
                    c7:fc:ff:45:b4:7e:2f:7a:ca:26:62:44:9a:76:57:
                    fe:6c:df:36:9c:3e:c8:a8:3c:62:34:c8:13:f9:48:
                    64:2b:a9:ed:fd:2f:0e:52:5c:7f:93:92:ed:d5:d5:
                    2b:88:b1:c4:b6:35:d7:74:00:0f:ef:b9:9b:3b:80:
                    98:c9
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Cert Type: 
                SSL Server
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication
            X509v3 Authority Key Identifier: 
                A1:99:57:5D:B1:10:A2:24:C8:FE:09:D4:48:24:B7:0F:2B:C5:D2:2B
            X509v3 Subject Key Identifier: 
                E3:6B:0E:75:91:EF:B6:06:AE:A3:18:AC:66:AD:F1:4C:90:85:F3:BE
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        52:12:13:4f:44:69:44:94:21:c6:b3:6c:79:bb:59:c0:96:09:
        98:4d:da:b5:74:f7:e9:b1:0b:a1:c0:ba:8a:17:20:97:40:5b:
        48:c7:1a:fb:ee:23:bc:f2:b2:cb:24:82:6c:6a:83:d2:68:a5:
        86:19:80:4b:fc:52:c2:22:6b:b3:80:50:da:c5:a2:dd:25:9a:
        7c:08:84:c6:67:b1:36:30:91:63:8a:85:84:28:4f:92:a2:d6:
        39:a6:6e:ff:ff:80:1b:4d:e7:26:8e:d5:04:55:b2:01:47:c5:
        88:d2:14:9f:e9:26:6e:be:c9:d1:94:f9:04:c7:c9:bf:08:eb:
        67:4f:3e:25:6d:1a:fa:37:c1:6f:e0:ea:88:87:11:0f:73:b7:
        a3:25:2a:7d:16:84:12:e6:42:b8:81:34:da:ba:66:44:dc:a6:
        9f:f2:6f:8a:ae:34:20:d5:d4:b6:0d:b2:b5:a2:22:53:8e:3e:
        39:ad:d3:35:a1:68:91:bb:f6:8e:80:31:cf:02:ae:4d:2c:d6:
        0c:c0:26:12:6d:0f:89:a6:d0:c1:48:91:4a:c4:e7:00:1d:0b:
        e6:c5:c5:a5:19:e1:b9:93:03:22:42:59:57:31:4c:ac:37:53:
        cd:16:19:7a:67:d8:7c:c0:b0:7f:0a:1e:29:c2:53:16:b6:ff:
        2b:40:8c:6c:ab:5e:a8:26:a8:0f:af:a2:6d:49:13:5b:ca:69:
        64:92:6b:1b:aa:3d:fa:07:90:92:38:ad:97:b4:22:11:26:87:
        61:7c:15:26:71:74:39:ef:93:b5:7b:74:47:67:e9:b4:53:8e:
        b4:a8:a4:da:a4:dd:68:79:5e:57:33:19:46:e7:f4:41:e3:20:
        dc:45:45:fe:4a:90:d9:e2:c1:0b:6d:3a:43:e0:1d:f5:a2:d2:
        d2:d9:68:c9:b4:b4:7f:62:85:8e:89:20:88:23:ab:f9:43:af:
        3f:0f:55:15:32:ff:03:bf:83:b0:ac:9c:3a:d2:56:06:f5:a7:
        ae:ab:45:0f:68:84:86:11:23:6f:7a:1f:0f:5a:0f:1a:84:e1:
        71:21:56:b6:61:98:8e:cc:f5:28:c3:70:8b:dc:9c:f8:61:d1:
        01:4e:e8:e2:18:33:82:c0:b0:cd:ee:6d:c6:94:96:78:fb:92:
        fc:14:a6:80:85:28:43:bf:62:08:a0:94:16:20:1a:2b:7d:c0:
        66:54:de:9b:ca:95:85:a7:dc:d6:a4:ea:ab:3e:0c:7a:e4:79:
        6e:34:17:7e:a4:9d:75:63:bf:08:66:dd:6a:79:33:fe:38:68:
        64:24:e8:0e:db:cc:92:13:44:de:0c:2f:64:5a:38:69:ee:98:
        33:78:af:ab:04:4d:83:15

 

Bonus Tip: Signing and Revoking a certificate using Intermediate CA

You can read more about certificate revocation at Revoke certificate and generate CRL OpenSSL

Revoking a certificate is the process of invalidating a previously issued SSL/TLS certificate before its expiration date. A certificate may need to be revoked for various reasons, such as:

  1. The private key associated with the certificate has been compromised.
  2. The certificate was issued fraudulently.
  3. The information in the certificate has changed, and it no longer accurately represents the subject.

Here I have generated some certificates under /certs folder and signed it using my intermediate CA.

# cat ~/myCA/intermediateCA/index.txt
V	240515083923Z		1000	unknown	/C=US/ST=California/L=San Francisco/O=Example Corp/OU=IT Department/CN=example.com

# cat ~/myCA/intermediateCA/serial
1001

# ls -l /certs/
total 12
-rw-r--r-- 1 root root 1769 May  6 14:09 server.cert.pem
-rw-r--r-- 1 root root 1037 May  6 14:08 server.csr.pem
-rw------- 1 root root 1704 May  6 14:07 server.key.pem

To revoke a certificate using OpenSSL, follow these steps:

Locate the certificate you want to revoke. You will need the certificate file (usually in PEM format, with the extension .crt, .cer, or .pem) or its serial number.

Revoke the certificate using the openssl ca command with the -revoke option. If you are revoking an end-entity (server or client) certificate signed by the intermediate CA, you will use the intermediate CA configuration file. For example:

openssl ca -config ~/myCA/openssl_intermediate.cnf -revoke /certs/server.cert.pem

Output:

Using configuration from /root/myCA/openssl_intermediate.cnf
Revoking Certificate 1000.
Data Base Updated
NOTE:
If you are revoking an intermediate CA certificate signed by the root CA, you will use the root CA configuration file. For example:
openssl ca -config openssl_root.cnf -revoke /path/to/intermediate_certificate.pem

After revoking the certificate, update the Certificate Revocation List (CRL) to include the newly revoked certificate. The CRL is a list of revoked certificates that clients can check to determine if a certificate is still valid.

Generate an updated CRL using the openssl ca command with the -gencrl option:

openssl ca -config ~/myCA/openssl_intermediate.cnf -gencrl -out ~/myCA/intermediateCA/crl/intermediate.crl.pem

Output:

Using configuration from /root/myCA/openssl_intermediate.cnf

The line with 1000 in the index.txt file represents a revoked certificate with the serial number 1000. The fields indicate the certificate's status (R for revoked), revocation date, expiration date, serial number, and subject information.

# cat ~/myCA/intermediateCA/index.txt
R 240515083923Z 230506084237Z 1000 unknown /C=US/ST=California/L=San Francisco/O=Example Corp/OU=IT Department/CN=example.com

The crlnumber file content 0101 represents the current CRL number, which is incremented each time a new Certificate Revocation List is generated.

# cat intermediateCA/crlnumber
0101

To get the list of revoked certificates from intermediate CA, we can use below command:

openssl crl -in ~/myCA/intermediateCA/crl/intermediate.crl.pem -text -noout

Output:

OpenSSL create Certificate Chain [Root & Intermediate CA]

 

Conclusion

In this guide, we walked through the process of creating a certificate chain using OpenSSL. The certificate chain is an essential component of Public Key Infrastructure (PKI), which allows secure communication between clients and servers over the internet. We covered the following topics:

  • Setting up a directory structure for the Root and Intermediate Certificate Authorities (CAs).
  • Creating configuration files (openssl.cnf) for the Root and Intermediate CAs.
  • Generating a Root CA private key and self-signed certificate.
  • Generating an Intermediate CA private key and Certificate Signing Request (CSR).
  • Signing the Intermediate CA's CSR with the Root CA, resulting in the Intermediate CA certificate.
  • Creating a Certificate Authority Bundle, which includes the Root and Intermediate CA certificates.
  • Generating a server private key and CSR.
  • Signing the server's CSR with the Intermediate CA, resulting in the server certificate.
  • Revoking a server certificate, updating the Certificate Revocation List (CRL), and managing the CRL numbers.

By following these steps, you can establish a secure and trustworthy certificate chain for your web services, allowing clients to verify the authenticity of your server and communicate securely.

 

References

I have used below external references for this tutorial guide
OpenSSL create certificate chain with root and intermediate certificate
Network Security with OpenSSL

 

Deepak Prasad

Deepak Prasad

He is the founder of GoLinuxCloud and brings over a decade of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels in various domains, from development to DevOps, Networking, and Security, ensuring robust and efficient solutions for diverse projects. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

64 thoughts on “OpenSSL create Certificate Chain [Root & Intermediate CA]”

    • You can try this script but it is quiet some time since I tested this so make sure to test before using it:

      #!/bin/bash
       
      if [ "$#" -ne 1 ]; then
          echo "Usage: $0 "
          exit 1
      fi
       
      BASEDIR="/root/tls"
      ROOTCA="${BASEDIR}/rootCA.pem"
      CABUNDLE="${BASEDIR}/ca-bundle.pem"
       
      mkdir -p "${BASEDIR}"
       
      # Generate the root CA key
      openssl genpkey -algorithm RSA -out "${BASEDIR}/rootCA.key"
      chmod 400 "${BASEDIR}/rootCA.key"
       
      # Generate the root CA certificate
      openssl req -x509 -new -nodes -key "${BASEDIR}/rootCA.key" -sha256 -days 1024 -out "${ROOTCA}" -subj "/C=US/ST=State/L=City/O=Organization/OU=Unit/CN=rootca.example.com"
       
      # Clear existing CA bundle
      echo -n > "${CABUNDLE}"
       
      # Add root CA to bundle
      cat "${ROOTCA}" >> "${CABUNDLE}"
       
      # Generate intermediate CA certificates
      for i in $(seq 1 $1); do
          # Generate the intermediate CA key
          openssl genpkey -algorithm RSA -out "${BASEDIR}/intermediateCA${i}.key"
          chmod 400 "${BASEDIR}/intermediateCA${i}.key"
       
          # Generate the intermediate CA certificate request
          openssl req -new -key "${BASEDIR}/intermediateCA${i}.key" -out "${BASEDIR}/intermediateCA${i}.csr" -subj "/C=US/ST=State/L=City/O=Organization/OU=Unit/CN=intermediateca${i}.example.com"
       
          # Use the root CA to sign the intermediate CA certificate
          openssl x509 -req -in "${BASEDIR}/intermediateCA${i}.csr" -CA "${ROOTCA}" -CAkey "${BASEDIR}/rootCA.key" -CAcreateserial -out "${BASEDIR}/intermediateCA${i}.pem" -days 500 -sha256
       
          # Add intermediate CA to bundle
          cat "${BASEDIR}/intermediateCA${i}.pem" >> "${CABUNDLE}"
      done

      You can run this using

      sh gen_cert_bundle.sh 2

      To read the CA bundle

      #!/bin/bash
       
      BUNDLE_FILE='/root/tls/ca-bundle.pem'
       
      awk -v cmd='openssl x509 -noout -subject -issuer -dates' '
          /-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/ {
              print | cmd
          }
          /-----END CERTIFICATE-----/ {
              close(cmd)
          }
      ' "${BUNDLE_FILE}"
      Reply
  1. I believe this conversation applies to the root CA config: https://github.com/openssl/openssl/issues/22966

    ... namely that the line under [ v3_intermediate_ca ]:
    authorityKeyIdentifier = keyid:always,issuer
    
    ... needs to be removed to work with later versions of openssl. The error I was getting, when trying to create an intermediate CA:
    
    ERROR: adding extensions in section v3_intermediate_ca
    139880504436544:error:2207707B:X509 V3 routines:v2i_AUTHORITY_KEYID:unable to get issuer keyid:crypto/x509v3/v3_akey.c:116:
    139880504436544:error:22098080:X509 V3 routines:X509V3_EXT_nconf:error in extension:crypto/x509v3/v3_conf.c:47:name=authorityKeyIdentifier, value=keyid:always,issuer

    Removing the aforementioned line from the rootCA config file fixes this.

    Reply
  2. Hello.

    I’ve scripted your steps. However, when I try to create the CSR for the intermediate authority I get the following error :
    “`bash
    Error: No objects specified in config file
    Error making certificate request
    “`

    I changed a bit the directory tree so that I can have multiple intermediates CA :
    ./myCA/root_CA
    ./myCA/intermediates_CA/XXX
    ./myCA/intermediates_CA/YYY

    Reply
  3. Getting below error while executing step4

    [host1: rootCA] $ openssl ca -config openssl_root.cnf -extensions v3_intermediate_ca -days 3650 -notext -md sha256 -in ~/myCA/intermediateCA/certs/intermediate.csr.pem -out ~/myCA/intermediateCA/certs/intermediate.cert.pem
    Using configuration from openssl_root.cnf
    Can't open /root/myCA/rootCA/private/ca.key.pem for reading, Permission denied
    139870465143936:error:0200100D:system library:fopen:Permission denied:crypto/bio/bss_file.c:69:fopen('/root/myCA/rootCA/private/ca.key.pem','r')
    139870465143936:error:2006D002:BIO routines:BIO_new_file:system lib:crypto/bio/bss_file.c:78:
    unable to load CA private key
    [host1: rootCA] $
    Reply
  4. Thanks for these steps! I dont require to create certs regularly and actually done this first time ever using your page.

    Only issue I have with the created certificates is that I cant see the root ca in intermediate crt’s (converted pem to crt after moving it to windows -if that matters) certificate path. Also cant see the cert chain in client crt’s certificate path.

    can you please point out what’s missing?

    Reply
  5. Good job 👏! Clear and concise ! Can’t wait to give it a try with self hosted smallstep CA !

    Thank you for your write up ! :))

    Reply
  6. Great post, very comprehensive.

    I think you have the part of “Signing the server’s CSR with the Intermediate CA, resulting in the server certificate.” missing from your post. Indeed, with your current setup, whenever I try to make a server certificate from the intermediate CA, and run an openssl verify, I get an error “Signing the server’s CSR with the Intermediate CA, resulting in the server certificate.” On trying to use the generated server CA in a server, I always find that I get the message from curl “SSL certificate problem: invalid CA certificate”.

    Reply
  7. Hello, thanks for the post, very useful.
    I need to create a certificate chain with SANs instead of Common Names, any tip ?
    Thanks in advance !

    Reply
  8. Hey,

    thank you for the guide, very well written 🙂

    I don’t know if someone already mentioned, but there is a small typo in step 4.

    You’re first creating the key for the intermediate CA, but then changing the ca.key of the root CA. That you already did in step 3 🙂

    So it should be something like that in step 4:

    chmod 400 ~/myCA/intermediateCA/private/intermediate.key.pem

    kind regards

    Reply

Leave a Comment