Payments are experiencing issues due to temporary restrictions in Russia. If your payment does not go through, please submit a support request.Our support team is available 24/7 — we are always here to help with hosting and server issues.We are now accepting requests for dedicated server rental and colocation services in our data center.Reminder: we recommend enabling backups for additional data protection.A new VPS/VDS lineup with NVMe storage and improved performance is now available.Maintenance work on some servers has been completed. All services are operating normally.
Article7 min readViews1

openssl verify: why the certificate chain fails to build without an intermediate CA

A practical guide for OpenSSL 3.0: distinguishing trust anchors from intermediates, reproducing errors 20, 2, and 62, reading depth, and separately verifying the DNS name.

Comments 0

The server and secure container are connected by a chain; the intermediate is examined under a magnifying glass
In this article

If a leaf certificate is signed by an intermediate CA, a single trusted root.pem is insufficient: OpenSSL must obtain intermediate.pem to build the path. Pass the root certificate via -CAfile and the intermediate via -untrusted. Then verify not only the 'OK' string but also the process exit code.

The -untrusted parameter does not declare the certificate malicious. It means the certificate can be used to build a chain but cannot be treated as an independent trust anchor. This distinction helps avoid treating error 20 by randomly adding certificates to the system store.

Prerequisites

  • Ubuntu 24.04.3 LTS and OpenSSL 3.0.13.

  • Three PEM files for the training chain: leaf.pem, intermediate.pem, and root.pem.

  • The leaf is issued for the DNS name backup.example.test; private keys are not needed to verify the chain.

  • Commands are executed in an isolated temporary directory without network access or production certificates.

  • Verification reproduced on October 22, 2026.

Which chain does OpenSSL build

The openssl verify documentation for version 3.0 defines -untrusted as a set of certificates for building the chain, while -show_chain displays the built path and marks certificates from this set as untrusted.

In terms of RFC 5280, the path links the certificate being verified to the trust anchor. In the educational model, this is leaf → intermediate → root. The validator checks signatures, validity periods, and certificate constraints, while the application may add separate requirements.

Trust Chain Tutorial: Leaf Certificate, Intermediate CA, and Root CA
The verification path is constructed from the leaf through the intermediate to the trusted root.

Error Without an Intermediate Certificate

openssl verify -show_chain -CAfile root.pem leaf.pem

CN = backup.example.test error 20 at 0 depth lookup: unable to get local issuer certificate error leaf.pem: verification failed

The command exited with code 2. The root is trusted, but the leaf is not signed directly by it. Without intermediate.pem, the validator cannot proceed from depth 0 to the next issuer. Do not conclude the root is incorrect based on a single error 20; first, determine which certificate is missing at the specified depth.

Full Chain via -untrusted

openssl verify -show_chain -CAfile root.pem -untrusted intermediate.pem leaf.pem

leaf.pem: OK Chain: depth=0: CN = backup.example.test (untrusted) depth=1: CN = Guide Intermediate CA (untrusted) depth=2: CN = Guide Root CA

The result is code 0. The untrusted label for the leaf and intermediate in the -show_chain output does not indicate an error: these certificates participated in building the path, but trust concluded at the root from -CAfile.

Why You Should Not Silently Make the Intermediate a Trust Anchor

If you pass intermediate.pem as the sole -CAfile without -partial_chain, the tutorial command returns error 2 at depth 1: the issuer of the intermediate certificate is not found. With -partial_chain, the same intermediate becomes an acceptable path termination, and the check passes. This is a different trust policy, not a repair of a missing chain.

Command

Result

Code

What was checked

root without intermediate

error 20 at depth 0

2

issuer certificate not found for leaf

root + -untrusted intermediate

OK, path to root

0

full test chain

intermediate as -CAfile

error 2 at depth 1

2

no intermediate issuer

intermediate + -partial_chain

OK up to intermediate

0

intermediate accepted as trust anchor

invalid root + intermediate

error 20 at depth 1

2

intermediate is not linked to a trusted root

Do not add -partial_chain just for a green result. Use it only when your policy truly trusts the specified intermediate CA as the path endpoint. For a standard server chain, maintain the distinction between the trust anchor and the certificates the server transmits to build the path.

Four openssl verify results for a test certificate chain
Error depth helps distinguish a missing intermediate, an incorrect root, and a name mismatch.

How to read depth and error number

Fragment

The meaning in the test chain

depth 0

the leaf certificate being checked

depth 1

intermediate CA

depth 2

root CA

error 20

local issuer not found at the specified depth

error 2

failed to get issuer certificate

Depth indicates where path building stopped. In a test without an intermediate, the error appeared at depth 0; with an incorrect root, it appeared at depth 1. The same error number can accompany different configuration causes, so save the full command, depth, subject, and exit code.

The host name is checked separately

A successful chain does not confirm that the certificate matches the required DNS name. For a test leaf with subjectAltName backup.example.test, two separate checks were performed.

openssl verify -CAfile root.pem -untrusted intermediate.pem -verify_hostname backup.example.test leaf.pem

leaf.pem: OK

openssl verify -CAfile root.pem -untrusted intermediate.pem -verify_hostname other.example.test leaf.pem

CN = backup.example.test error 62 at 0 depth lookup: hostname mismatch error leaf.pem: verification failed

The matching name returned code 0, while the other name returned error 62 and code 2. Therefore, the "chain is correct" report and the "certificate matches the name" report must remain two explicit checks.

Protocol of the reproducible test

  • Root CA created as self-signed with CA:TRUE and keyCertSign.

  • Intermediate CA signed by root with pathlen:0.

  • Leaf signed by intermediate, contains CA:FALSE, serverAuth, and DNS:backup.example.test.

  • Without intermediate, received error 20 and exit 2.

  • With root and -untrusted intermediate, received OK and exit 0.

  • Intermediate used as -CAfile without -partial_chain yielded error 2 and exit 2; with -partial_chain, OK and exit 0.

  • Invalid root yielded error 20 at depth 1 and exit 2.

  • Correct name passed with exit 0; incorrect name yielded error 62 and exit 2.

All keys and certificates were created solely within a temporary directory and removed after the test. They do not belong to the client or the domain of the production system. This example verifies the behavior of a local validator, not the configuration of a specific web server.

Verification order for the working chain

  1. Record the OpenSSL version, the leaf being verified, the expected DNS name, and the trusted root store.

  2. Obtain the intermediate from a trusted source and do not mix it with the private key.

  3. First run verify with explicit -CAfile and -untrusted, then save -show_chain and the exit code.

  4. If the error indicates depth 0, check for the presence of a leaf issuer; if depth 1, verify the link between the intermediate and the selected root.

  5. Separately run -verify_hostname or -verify_ip depending on the address type.

  6. Do not use -partial_chain, -no_check_time, or disable checks without a documented requirement.

  7. After local verification, check the actual set of certificates the service provides and repeat the verification using the TLS client you use.

For a public site, the actual certificate, validity period, and provided chain can be additionally verified via the built-in SSL check. The local model and the external server response solve different tasks.

Example boundaries

  • The test does not perform a TLS handshake, SNI, or network request.

  • OCSP, CRL, and URL availability for revocation are not checked.

  • The order of certificates in the configuration of a specific nginx, Apache, or control panel is not analyzed.

  • Test certificates are valid for three days; this is not a recommendation for the validity period of production certificates.

  • Chain verification does not confirm that the leaf certificate matches the private key, nor does it replace file permission checks.

  • Error codes and messages refer to OpenSSL 3.0.13; the wording may differ in other versions.

Readiness criterion: the command with the expected root and explicit intermediate completes with code 0, -show_chain ends with the correct trust anchor, and a separate name check also passes. Any error requires analyzing the depth and source of the certificate, not randomly extending the trusted store.

Discussion 0

Share your experience and ask questions. Comments without links appear after editorial review.

No comments yet. Start the discussion.