OpenSSL create certificate chain with Root & Intermediate CA

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

 

I have already written multiple articles on OpenSSL, I would recommend you to also check them for more overview on openssl examples:

Advertisement

 

Step 1: Install OpenSSL

On RHEL/CentOS 7/8 you can use yum or dnf respectively while on Ubuntu use apt-get 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.
[root@centos8-1 ~]# yum -y install openssl

 

Step 2: OpenSSL encrypted data with salted password

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 for openssl encd data with salted password to encrypt the password file. So I will not repeat the steps here again.

We will use the same encrypted password file for all our examples in this article to demonstrate openssl create certificate chain examples.

 

Step 3: 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/tls/ to store our certificates.

Advertisement

Create a parent directory to store the certificates

[root@centos8-1 ~]# mkdir /root/tls
[root@centos8-1 ~]# cd /root/tls

Within the CA’s root directory, we need to create two sub directories:

certs: This will be used to keep copies of all of the certificates that we issue with our CA.
private: This will be used to keep a copy of the CA certificate’s private key.

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
[root@centos8-1 tls]# mkdir certs private

Besides key generation, we will create three files that our CA infrastructure will need.

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, so we must pad the value by prepending a zero to it.

[root@centos8-1 tls]# echo 01 > serial

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. Since no certificates have been issued at this point and OpenSSL requires that the file exist, we’ll simply create an empty file.

[root@centos8-1 tls]# touch index.txt

Check the list of contents under /root/tls

[root@centos8-1 tls]# ls -l
total 32
drwxr-xr-x 2 root root 4096 Apr  8 22:29 certs
-rw-r--r-- 1 root root    0 Apr  9 03:36 index.txt
-rw-r--r-- 1 root root   32 Apr  8 23:25 mypass.enc
drwxr-xr-x 2 root root 4096 Apr  9 03:34 private
-rw-r--r-- 1 root root    3 Apr  9 03:37 serial

 

Advertisement

Step 4: Configure openssl.cnf for Root CA Certificate

We will have a default configuration file openssl.cnf in RHEL/CentOS 7/8 under /etc/pki/tls/openssl.cnf which is added by the openssl rpm. We will copy this file to your custom certificate location i.e. /root/tls and will modify the content of this file to create Root CA Certificate

HOME                    = .
RANDFILE                = $ENV::HOME/.rnd
oid_section             = new_oids

openssl_conf = default_modules

[ default_modules ]
ssl_conf = ssl_module

[ ssl_module ]
system_default = crypto_policy

[ crypto_policy ]
.include /etc/crypto-policies/back-ends/opensslcnf.config

[ new_oids ]

The OpenSSL command for the CA functions is aptly named ca , and so the first section that we’re interested in is named ca. For our purposes, this section is quite simple, containing only a single key: default_ca . The value is the name of a section containing the configuration for the default CA. The [ CA_default ] section contains a range of defaults. Make sure you declare the directory you chose earlier /root/tls.

The x509_extensions key specifies the name of a section that will contain the extensions to be added to each certificate issued by our CA.

[ ca ]
default_ca      = CA_default            # The default ca section

[ CA_default ]
dir             = /root/tls             # Where everything is kept
certs           = $dir/certs            # Where the issued certs are kept
database        = $dir/index.txt        # database index file.
                                        # several certs with same subject.
new_certs_dir   = $dir/certs            # default place for new certs.
certificate     = $dir/certs/cacert.pem       # The CA certificate
serial          = $dir/serial           # The current serial number
crlnumber       = $dir/crlnumber        # the current crl number
                                        # must be commented out to leave a V1 CRL
private_key     = $dir/private/cakey.pem # The private key

name_opt        = ca_default            # Subject Name options
cert_opt        = ca_default            # Certificate field options

default_days    = 365                   # how long to certify for
default_crl_days= 30                    # how long before next CRL
default_md      = sha256                # use SHA-256 by default
preserve        = no                    # keep passed DN ordering
policy          = policy_match

 

The policy key specifies the name of a section that will be used for the default policy.
A policy definition is a set of keys with the same name as the fields in a certificate’s distinguished name. For each key or field, there are three legal values: match, supplied, or optional.

  • match: means that the field by that name in a certificate request must match the same field in the CA’s certificate.
  • supplied: means that the certificate request must contain the field.
  • optional: means that the field is not required in the certificate request.

We will apply policy_match for creating root CA certificates so we have added this as a default value for policy under CA_default.

[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

 

And policy_anything for creating Intermediate CA certificates

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

 

The values under [ req ] section are applied when creating Certificate Signing Requests (CSR) or Certificates. The x509_extensions key specifies the name of a section that contains the extensions that we want included in the certificate.

[ req ]
default_bits            = 4096
default_md              = sha256
default_keyfile         = privkey.pem
distinguished_name      = req_distinguished_name
x509_extensions         = v3_ca
string_mask             = nombstr

 

The eq_distinguished_name key determine how OpenSSL gets the information it needs to fill in the certificate’s distinguished name. I have given few default values while the Common Name must be supplied as we have defined under policy key.

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = IN
countryName_min                 = 2
countryName_max                 = 2
stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = Some-State
localityName                    = Locality Name (eg, city)
localityName_default            = BANGALORE
0.organizationName              = Organization Name (eg, company)
0.organizationName_default      = GoLinuxCloud
organizationalUnitName          = Organizational Unit Name (eg, section)
commonName                      = Common Name (eg, your name or your server\'s hostname)
commonName_max                  = 64
emailAddress                    = Email Address
emailAddress_max                = 64

 

These are the extensions we will use with openssl create certificate chain. We will use v3_ca extension to create root CA certificate and v3_intermediate extension for intermediate CA certificate.

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.
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true

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

 

Step 5: Generate Root CA Private Key

  • We will create root CA key using 4096 bits and 3DES encryption
  • I am using my encrypted password file to provide the passphrase for the private key
  • We will store this private key under /root/tls/private
[root@centos8-1 tls]# openssl genrsa -des3 -passout file:mypass.enc -out private/cakey.pem 4096
Generating RSA private key, 4096 bit long modulus (2 primes)
.......................................................................++++
.......................++++
e is 65537 (0x010001)
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.

 

OpenSSL verify Root CA key

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

[root@centos8-1 tls]# openssl rsa -noout -text -in private/cakey.pem -passin file:mypass.enc
RSA Private-Key: (4096 bit, 2 primes)
<Output trimmed>

 

Step 6: Create your own 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

[root@centos8-1 tls]# openssl req -new -x509 -days 3650 -passin file:mypass.enc -config openssl.cnf -extensions v3_ca -key private/cakey.pem -out certs/cacert.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [IN]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) [BANGALORE]:
Organization Name (eg, company) [GoLinuxCloud]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:centos8-1
Email Address []:admin@golinuxcloud.com

To change the format of the certificate to PEM format

[root@centos8-1 tls]# openssl x509 -in certs/cacert.pem -out certs/cacert.pem -outform PEM

 

OpenSSL verify Certificate

Execute the below command for openssl verify root CA certificate

[root@centos8-1 tls]# openssl x509 -noout -text -in certs/cacert.pem
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            55:b2:c2:5e:7b:95:4a:05:78:32:82:6f:b1:60:a0:92:03:96:0b:30
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = IN, ST = Some-State, L = BANGALORE, O = GoLinuxCloud, CN = centos8-1, emailAddress = admin@golinuxcloud.com
        Validity
            Not Before: Apr 11 11:34:22 2020 GMT
            Not After : Apr  9 11:34:22 2030 GMT
        Subject: C = IN, ST = Some-State, L = BANGALORE, O = GoLinuxCloud, CN = centos8-1, emailAddress = admin@golinuxcloud.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (4096 bit)
<Output trimmed>

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.

        X509v3 extensions:
            X509v3 Subject Key Identifier:
                0B:FE:44:49:A9:90:96:9A:19:17:56:55:2D:70:B0:0D:D8:5D:9A:73
            X509v3 Authority Key Identifier:
                keyid:0B:FE:44:49:A9:90:96:9A:19:17:56:55:2D:70:B0:0D:D8:5D:9A:73

            X509v3 Basic Constraints: critical
                CA:TRUE

 

Step 7: Create OpenSSL Intermediate CA directory structure

Now to complete setup of openssl create certificate chain, we will also need intermediate certificate for the CA bundle. We will create new directory structure /root/tls/intermediate under our parent folder /root/tls to keep both the certificate files separate.

[root@centos8-1 tls]# mkdir /root/tls/intermediate
[root@centos8-1 tls]# cd /root/tls/intermediate

We will also create sub directories under /root/tls/intermediate to store our keys and certificate files. We will also need a serial and index.txt file as we created for our Root CA Certificate.

[root@centos8-1 intermediate]# mkdir certs csr private
[root@centos8-1 intermediate]# touch index.txt
[root@centos8-1 intermediate]# echo 01 > serial

Add a crlnumber file to the intermediate CA directory tree. crlnumber is used to keep track of certificate revocation lists.

[root@centos8-1 intermediate]# echo 01 > /root/tls/intermediate/crlnumber

 

Step 8: Configure openssl.cnf for Intermediate CA Certificate

Copy the openssl.cnf used for our Root CA Certificate from /root/tls/openssl.cnf to /root/tls/intermediate/openssl.cnf. Below are the options we have been changed compared to the root CA certificate configuration file:

dir             = /root/tls/intermediate               # Where everything is kept
certificate     = $dir/certs/intermediate.cacert.pem   # The CA certificate
private_key     = $dir/private/intermediate.cakey.pem  # The private key
policy          = policy_anything

 

Step 9: Generate Intermediate CA key

Generate intermediate CA key ca-intermediate.key.using openssl genrsa with 3DES encryption and our encrypted passphrase file to avoid any password prompt.

[root@centos8-1 tls]# openssl genrsa -des3 -passout file:mypass.enc -out intermediate/private/intermediate.cakey.pem 4096
Generating RSA private key, 4096 bit long modulus (2 primes)
...........................................................................++++
............................................................................++++
e is 65537 (0x010001)

 

Step 10: Create immediate CA Certificate Signing Request (CSR)

  • Use the intermediate CA key to create a certificate signing request (CSR).
  • The details should generally match the root CA.
  • The Common Name, however, must be different.

Next we will create intermediate CA certificate signing request (CSR) under /root/tls/intermediate/csr with expiry value lesser than the root CA certificate

[root@centos8-1 tls]# openssl req -new -sha256 -config intermediate/openssl.cnf -passin file:mypass.enc  -key intermediate/private/intermediate.cakey.pem -out intermediate/csr/intermediate.csr.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [IN]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) [BANGALORE]:
Organization Name (eg, company) [GoLinuxCloud]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:centos8-1 Intermediate CA
Email Address []:admin@golinuxcloud.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

 

Step 11: Sign and generate immediate CA certificate

Now the last step before we conclude openssl create certificate chain, we need to create immediate CA certificate using our Certificate Signing request which we created in above step. We will use v3_intermediate_ca extension from /root/tls/openssl.cnf to create the intermediate CA certificate under /root/tls/intermediate/certs/intermediate.cacert.pem

[root@centos8-1 tls]# openssl ca -config openssl.cnf -extensions v3_intermediate_ca -days 2650 -notext -batch -passin file:mypass.enc -in intermediate/csr/intermediate.csr.pem -out intermediate/certs/intermediate.cacert.pem
Using configuration from openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Apr  9 13:40:23 2020 GMT
            Not After : Jul 12 13:40:23 2027 GMT
        Subject:
            countryName               = IN
            stateOrProvinceName       = Some-State
            organizationName          = GoLinuxCloud
            commonName                = centos8-1 Intermediate CA
            emailAddress              = admin@golinuxcloud.com
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                A4:07:28:66:F1:71:CD:0B:62:2E:01:8C:6B:04:5B:CC:D0:96:24:89
            X509v3 Authority Key Identifier:
                keyid:0B:FE:44:49:A9:90:96:9A:19:17:56:55:2D:70:B0:0D:D8:5D:9A:73

            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
            X509v3 Key Usage: critical
                Digital Signature, Certificate Sign, CRL Sign
Certificate is to be certified until Jul 12 13:40:23 2027 GMT (2650 days)

Write out database with 1 new entries
Data Base Updated

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.

[root@centos8-1 tls]# cat index.txt
V       270712134023Z           01      unknown /C=IN/ST=Some-State/O=GoLinuxCloud/CN=centos8-1 Intermediate CA/emailAddress=admin@golinuxcloud.com

 

OpenSSL verify Certificate

Verify the Intermediate CA Certificate content

[root@centos8-1 tls]# openssl x509 -noout -text -in intermediate/certs/intermediate.cacert.pem
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1 (0x1)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = IN, ST = Some-State, L = BANGALORE, O = GoLinuxCloud, CN = centos8-1, emailAddress = admin@golinuxcloud.com
        Validity
            Not Before: Apr  9 13:40:23 2020 GMT
            Not After : Jul 12 13:40:23 2027 GMT
        Subject: C = IN, ST = Some-State, O = GoLinuxCloud, CN = centos8-1 Intermediate CA, emailAddress = admin@golinuxcloud.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (4096 bit)

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

[root@centos8-1 tls]# openssl verify -CAfile certs/cacert.pem intermediate/certs/intermediate.cacert.pem
intermediate/certs/intermediate.cacert.pem: OK

To convert the format of the Certificate to PEM format

[root@centos8-1 tls]# openssl x509 -in intermediate/certs/intermediate.cacert.pem -out intermediate/certs/intermediate.cacert.pem -outform PEM

 

Step 12: 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.

[root@centos8-1 tls]# cat intermediate/certs/intermediate.cacert.pem certs/cacert.pem > intermediate/certs/ca-chain-bundle.cert.pem

 

OpenSSL verify Certificate Chain

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

[root@centos8-1 tls]# openssl verify -CAfile certs/cacert.pem intermediate/certs/ca-chain-bundle.cert.pem 
intermediate/certs/ca-chain-bundle.cert.pem: OK

To verify certificate chain for online pages such as Google:

[root@centos8-1 certs]# openssl s_client -quiet -connect google.com:443
depth=2 OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
verify return:1
depth=1 C = US, O = Google Trust Services, CN = GTS CA 1O1
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google LLC, CN = *.google.com
verify return:1

To show certificates from the certificate chain for Google:

[root@centos8-1 certs]# openssl s_client -showcerts -connect google.com:443
CONNECTED(00000003)
depth=2 OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
verify return:1
depth=1 C = US, O = Google Trust Services, CN = GTS CA 1O1
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google LLC, CN = *.google.com
verify return:1
<Output trimmed>

 

Conclusion

In this tutorial we learned how to create certificate chain using openssl with root and intermediate certificate. You can add upto "n" number of intermediate certificates in the certificate chain depending upon your requirement.

Lastly I hope the steps from the article for openssl create certificate chain with Root and Intermediate Certificate on Linux was helpful. So, let me know your suggestions and feedback using the comment section.

Next we will use this Root and Intermediate CA bundle to sign and generate server and client certificates to configure end to end encryption for Apache web server in Linux.

 

References

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

 

Related Searches: Openssl create certificate chain, root ca certificate, intermediate ca certificate, verify certificate chain, create ca bundle, verify ca certificate, openssl verify certificate, openssl view certificate, openssl get certificate info

Didn't find what you were looking for? Perform a quick search across GoLinuxCloud

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 either use the comments section or contact me form.

Thank You for your support!!

31 thoughts on “OpenSSL create certificate chain with Root & Intermediate CA”

  1. openssl ca -config openssl.cnf -extensions v3_intermediate_ca -days 2650 -notext -batch -passin file:mypass.enc -in intermediate/csr/intermediate.csr.pem -out intermediate/certs/intermediate.cacert.pem

    My Version:
    andre@Heimserver:~/Zertifikat Baustelle/root/tls$ openssl ca -config apache_intermediate_ca.cnf -extensions v3_intermediate_ca -days 3650 -notext -batch -passin file:andrepass.enc -in intermediate/csr/apache_intermediate.csr.pem -out intermediate/certs/apache_intermediate_ca.crt
    Using configuration from apache_intermediate_ca.cnf
    Could not open file or uri /root/tls/private/andre-root-ca-key.pem for loading CA private key
    40C711AC187F0000:error::system library:file_open:Permission denied:crypto/store/loader_file.c:919:calling stat(/root/tls/private/andre-root-ca-key.pem)
    Unable to load CA private key

    Thanks for the great instructions and the wasted lifetime

    Reply
  2. I found the bug, it was my fault. Sorry
    Nice instructions, but there is a small mistake:
    OpenSSL verify Certificate Chain
    After openssl create certificate chain, to verify certificate chain use below command:
    [root@centos8-1 tls]# openssl verify -CAfile certs/cacert.pem intermediate/certs/intermediate.cacert.pem
    Not like this, but like this:
    [root@centos8-1 tls]# openssl verify -CAfile certs/cacert.pem intermediate/certs/ca-chain-bundle.cert.pem

    Reply
    • Thank you for highlighting this. You are right, the provided text and commands didn’t matched so I have updated the command snippet. We were actually supposed to verify the certificate chain instead of intermediate cert

      Reply
  3. Thanks for providing this. I have an implementation question however as we have run into variations on where the intermediary certificates should be vs the root CA certificates. There is a school of thought that the web server certificate should include the intermediary CA chain with it, and present it to clients, and the client’s trust store (CA Bundle) should only contain the root CA. It becomes problematic to have to overload a complex private CA heirarchy across all client nodes truststores (CA bundles) as opposed to only providing the root CA.

    Is anyone else seeing this used as a practice?

    Reply
    • Do you mean you want to add certificates to existing bundle -in which case you have to add the new CA cert the same order as it was added earlier
      For creating new CA chain bundle you can follow the same steps as I have mentioned here.

      Reply
      • hi – thanks for the reply.

        no, i meant create a server certificate that uses the chain in a wildcard certificate i bought from a commercial CA. it isn’t really possible of course. i asked before i really understood the concepts involved.

        Reply
  4. Hi, so once I have the root ca and the intermediate ca, how do i create a client certificate using the intermediate ca in way which the intermediate ca will actually log the creation of the client certificate in the intermediate/index.txt file?

    I have followed the instructions here https://www.golinuxcloud.com/openssl-create-client-server-certificate/ and I can get a working client certificate but the intermediate ca has not logged the fact that it was signed in the index.txt file?

    Am i right in thinking tls/openssl.cnf is for root ca, intermediate/openssl.cnf is for the intermediate ca, and I now need to create a new openssl.cnf for ultimate client certificates made using the intermediate ca? Is this right? or do I just use the intermediate/openssl.cnf?

    Thanks for your help.

    Reply
    • In the above example I was using openssl.cnf from the root ca so the index.txt of the root CA was updated, you can use intermediate/openssl.cnf in step 11 so index.txt of the intermediate certificate would be updated but that wouldn’t make sense here as we were signing the intermediate certificate.

      I have used a different command to sign the certificates in the client-server certificate article which you shared, to update the index.txt every time you sign a certificate using intermediate certificate you can use

      openssl ca -config <path to your openssl.cnf> [-extensions <if any>] -days 3650 -notext -batch -in <your CSR> -out <CERTIFICATE_to_BE_CREATED>

      For example, here I have signed two server certificates:

      ~]# cat index.txt
      V       280512060848Z           01      unknown /C=IN/ST=KARNATAKA/O=GoLinuxCloud/OU=R&D/CN=server-3
      V       280512061340Z           02      unknown /C=IN/ST=KARNATAKA/O=GoLinuxCloud/OU=R&D/CN=server-5
      Reply
  5. STEP 11.
    USING COMMAND LINE

    C:\Users\Lenovo\root\tls>openssl ca -config openssl.cnf -extensions v3_intermediate_ca -days 3650 -notext -batch -in intermediate/csr/intermediate.csr.pem -out intermediate/certs/intermediate.cacert.pem
    -----------------------------------------------------------------
    ERROR
    Using configuration from openssl.cnf
    Can't open /root/tls/private/cakey.pem for reading, No such file or directory
    7760:error:02001003:system library:fopen:No such process:crypto\bio\bss_file.c:69:fopen('/root/tls/private/cakey.pem','r')
    7760:error:2006D080:BIO routines:BIO_new_file:no such file:crypto\bio\bss_file.c:76:
    unable to load CA private key

    can you help

    Reply
        • You are using the provided openssl.cnf which uses /root/tls/private to store the private key. Check private_key variable in the openssl.cnf file.
          I can only assume that you have not created the private key as explained in Step 5 or you have created it in a different location or with a different name.

          Reply
          • Hello
            Even am getting the same error at step 11.The only different thing I did is while creating encrypted password file as the pbkdf2 was not supported “openssl enc -aes256 -md sha512 -salt -in mypass -out mypass.enc “.
            It works smoothly till step 11.Kindly Help.

            Reply
            • For the above failure, the error message is quite clear

              Can't open /root/tls/private/cakey.pem for reading, No such file or directory

              Have you copied the openssl.cnf as I have explained in step 8. If yes please share the exact error message what you see.
              As per my setup you should be inside /root/tls or you can provide the absolute path of openssl.cnf created for intermediate certificate.

              Reply
  6. Congratz for the article. Very useful.

    However, I would like to know how can I create a chain with 2 intermediate certificates. It is not clear to me what have to be changed on the intermediate CA creation to create the second intermediate CA.
    What I want to achieve is something like this:
    root CA –> Intermediate CA 1 –> Intermediate CA 2

    I took the steps from 7 to 12 to try to create the second intermediate CA. But I guess I ended up creating something like this:
    root CA –> Intermediate CA 1
    root CA –> Intermediate CA 2

    Can you please help me on that?

    Reply
    • In my example I have used the primary root CA to sign the IM CA1 so in your case you can create further sub directory inside IM CA1 and then use IM CA1 to sign your IM CA2 to make it root CA -> IM CA1 -> IM CA2

      Reply
      • Yeah, that was one of my attempts to accomplish that, but when I run openssl verify using the 3 CAs I am getting an error.

        [root@poargs-ws16 intermediate]# openssl verify -CAfile /root/tls/certs/cacert.pem certs/intermediate.cacert.pem intermediate2/certs/intermediate2.cacert.pem
        /root/tls/intermediate/certs/intermediate.cacert.pem: OK
        C = BR, ST = XX, L = XX, O = XX, OU = XX, CN = Intermediate CA II, emailAddress = XX
        error 20 at 0 depth lookup: unable to get local issuer certificate
        error /root/tls/intermediate/intermediate2/certs/intermediate2.cacert.pem: verification failed

        Searching on internet I’ve found I could use the flag -untrusted on openssl. And using that, I get a successful check. Is that the correct way to verify a chain of certificates?

        [root@poargs-ws16 intermediate]# openssl verify -CAfile /root/tls/certs/cacert.pem -untrusted certs/intermediate.cacert.pem intermediate2/certs/intermediate2.cacert.pem
        /root/tls/intermediate/intermediate2/certs/intermediate2.cacert.pem: OK

        Also, if I run the verify on the bundle, it returns me a failure as well:

        [root@poargs-ws16 intermediate]# openssl verify -CAfile /root/tls/certs/cacert.pem /root/tls/certs/ca-chain-bundle.cert.pem
        C = BR, ST = XX, L = XX, O = XX, OU = XX, CN = Intermediate CA II, emailAddress = XX
        error 20 at 0 depth lookup: unable to get local issuer certificate
        error /root/tls/certs/ca-chain-bundle.cert.pem: verification failed

        I will retry the procedures to see if I get anything different, but if you could please advise I would be glad.

        Thanks again.

        Reply
        • Your observations are interesting, I would have assumed that it will work. I remember creating multi level ca bundle and the verification had worked. Let me do some test runs myself and then may be I can guide better. If you are able to crack this before me then please do share here.

          Reply
        • I think I found the problem. I am setting pathlen:0 under v3_intermediate_ca which means that no non-self-issued intermediate CA certificates may follow in a valid certification path. So either we can give a higher value for this or just remove it and create another section for second intermedtiate CA

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

          With this change I was able to create two level of intermediate CA certificate:

          [root@server tls]# cat index.txt
          V       280709181246Z           01      unknown /C=IN/ST=Karnataka/O=GoLinuxCloud/CN=ca-server Intermediate CA I/emailAddress=admin@golinuxcloud.com
          V       251013181639Z           02      unknown /C=IN/ST=Karnataka/O=GoLinuxCloud/CN=ca-server Intermediate CA II/emailAddress=admin@golinuxcloud.com

          The verification was also success:

          [root@server tls]# openssl verify -CAfile certs/cacert.pem intermediate_1/intermediate_2/certs/intermediate_2.cacert.pem intermediate_1/certs/intermediate_1.cacert.pem
          intermediate_1/intermediate_2/certs/intermediate_2.cacert.pem: OK
          intermediate_1/certs/intermediate_1.cacert.pem: OK

          Also from the ca-bundle:

          [root@server tls]# cat intermediate_1/intermediate_2/certs/intermediate_2.cacert.pem  intermediate_1/certs/intermediate_1.cacert.pem certs/cacert.pem > certs/ca-chain-bundle.cert.pem
          [root@server tls]# openssl verify -CAfile certs/cacert.pem certs/ca-chain-bundle.cert.pem
          certs/ca-chain-bundle.cert.pem: OK

          My directory structure (if it helps)

          [root@server tls]# tree .
          .
          ├── certs
          │   ├── 01.pem
          │   ├── 02.pem
          │   ├── cacert.pem
          │   └── ca-chain-bundle.cert.pem
          ├── index.txt
          ├── index.txt.attr
          ├── index.txt.attr.old
          ├── index.txt.old
          ├── intermediate_1
          │   ├── certs
          │   │   └── intermediate_1.cacert.pem
          │   ├── crlnumber
          │   ├── csr
          │   │   └── intermediate_1.csr.pem
          │   ├── index.txt
          │   ├── intermediate_2
          │   │   ├── certs
          │   │   │   └── intermediate_2.cacert.pem
          │   │   ├── csr
          │   │   │   └── intermediate_2.csr.pem
          │   │   ├── index.txt
          │   │   ├── openssl.cnf
          │   │   ├── private
          │   │   │   └── intermediate_2.cakey.pem
          │   │   └── serial
          │   ├── openssl.cnf
          │   ├── private
          │   │   └── intermediate_1.cakey.pem
          │   └── serial
          ├── openssl.cnf
          ├── private
          │   └── cakey.pem
          ├── serial
          └── serial.old
          
          10 directories, 25 files
          Reply
          • I don’t know if I did it right, but I am still having issues with intermediate 2.

            [root@poargs-ws16 certificates]# openssl verify -CAfile certs/cacert.pem ica1/certs/ica1.cacert.pem ica1/ica2/certs/ica2.cacert.pem
            ica1/certs/ica1.cacert.pem: OK
            C = BR, ST = XX, L = XX, O = XX, OU = XX, CN = ICA II, emailAddress = XX
            error 20 at 0 depth lookup: unable to get local issuer certificate
            error ica1/ica2/certs/ica2.cacert.pem: verification failed

            What I did was to change v3_intermediate_ca and remove the pathlen:0 as basic constraint. The sections is like this:

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

            How did you o it? Did you increase pathlen to 2 or something like that?

            Reply
            • Sorry for the confusion, I made a mistake in my last trial. I used the root CA to sign both the intermediate certificate which is why the following command was successful

              [root@server tls]# openssl verify -CAfile certs/cacert.pem intermediate_1/intermediate_2/certs/intermediate_2.cacert.pem intermediate_1/certs/intermediate_1.cacert.pem
              intermediate_1/intermediate_2/certs/intermediate_2.cacert.pem: OK
              intermediate_1/certs/intermediate_1.cacert.pem: OK

              This is wrong because root CA is not supposed to sign level 2 CA. I have corrected it and re-verified and it worked. I have mailed you the detailed steps with all the output. Let me know if you are still facing any issues.

              Reply
              • Just to add one more thing, the expiry of each intermediate certificate should be less than it’s parent CA certificate

                Reply
  7. Thank you very much for working on that.
    In fact, you did exactly what I ended up doing after many attempts of making it work. Which was to change the the way to run the verify command.
    At first I thought you could add all certificates after -CAfile parameter and then openssl would verify the last CA on the chain in order.

    openssl verify -CAfile  rootCA.pem intermediateCA1.pem intermediateCA2.pem

    But, as you did:

    [root@server intermediate_1]# cat intermediate/certs/intermediate.cacert.pem certs/cacert.pem > intermediate/certs/ca-chain-bundle.cert.pem
    [root@server intermediate_1]# cd ..
    [root@server tls]# openssl verify -CAfile  certs/ca-chain-bundle.cert.pem intermediate_1/certs/intermediate_1.cacert.pem
    intermediate_1/certs/intermediate_1.cacert.pem: OK

    I created one bundle with rootCA.pem and intermediateCA1.pem and then verified against intermediateCA2.pem and that returned OK.

    openssl -verify -CAfile CAbundle-rootca-intermediateCA1.pem intermediateCA2.pem

    The same pattern worked when I created the certificate to be used on my server. I’ve run something like:

    cat intermediateCA2.pem intermediateCA1.pem rootCA.pem > chainCA.pem
    openssl -verify -CAfile chainCA.pem server.pem

    Then I configured my server with the server certificate and my client with the bundle and everythink worked like a charm.

    Thanks you so much for your help,

    P.S.: I will try to create a script to automate this process and if you want I can share it here. But I strongly recommend people to follow the manual steps. If I had a script, I would not know what I’ve learned by doing everything manually.

    Reply

Leave a Comment