A certificate deployment can fail even when the leaf certificate looks correct. The key may match, the subject alternative name may be present, and the expiration date may be acceptable, but a client still needs a chain from that leaf to a trust anchor. The intermediate certificate is the piece most often missed when a service package, load balancer, or secret contains only the leaf certificate.
OpenSSL’s verify command gives that problem a small, offline test boundary. This guide builds a disposable three-certificate hierarchy, proves that a leaf without its intermediate fails, and then supplies the intermediate explicitly to prove that the same leaf can be validated from the chosen root. The successful result is a repeatable pre-deployment check: missing chain material returns a nonzero status, while the complete chain reports OK.
The procedure was tested locally on Linux AMD64 with OpenSSL 3.5.6. It created short-lived synthetic certificates for example.test, used no network listener, and did not touch a system trust store or a real service key. The incomplete-chain check exited 2 with unable to get local issuer certificate; adding the intermediate with -untrusted made the leaf pass with exit status 0. Use a disposable directory and synthetic names for this check. Do not copy production private keys into a CI workspace or a troubleshooting archive.
OpenSSL 3.5.7 was released as a security patch on 9 June 2026. Its release notes include fixes rated up to High severity. That is a reason to check the version supplied by the operating-system package or TLS image before using this procedure, not a reason to treat a local chain check as evidence that an older runtime is safe. Upgrade decisions need the release notes, distribution advisories, and an application compatibility test.
Make the trust boundary explicit
A chain has different roles. The root certificate is the trust anchor: the verifier accepts it because it was supplied through a trust store or a specific -CAfile. An intermediate CA is allowed to sign another certificate but is not inherently trusted just because it appears beside a leaf. The leaf is the identity a service presents, usually with a DNS name in subjectAltName.
That distinction is why the command below uses two separate inputs. -CAfile root.crt names the trust anchor. -untrusted intermediate.crt supplies a possible linking certificate that OpenSSL may use to construct a chain, but it does not turn that intermediate into a root. Treat this as a deployment check, not as an instruction to add arbitrary downloaded certificates to a trust store.
Create an empty review directory with restrictive permissions. The shell commands generate new keys, so run them only where the resulting private keys can be removed afterward.
install -d -m 700 <review-directory>
cd <review-directory>
Create a temporary root and an intermediate signing request. The names are deliberately synthetic. The short validity period prevents these test certificates from being mistaken for service credentials.
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 2 \
-subj '/CN=Root CA - example.test' \
-keyout root.key -out root.crt
openssl req -newkey rsa:2048 -nodes -sha256 \
-subj '/CN=Intermediate CA - example.test' \
-keyout intermediate.key -out intermediate.csr
The -nodes option leaves these disposable test keys unencrypted. It is convenient for a noninteractive lab, but it is not a default for a production private key. A deployed key needs the storage, permissions, and secret-management controls appropriate to the service that owns it.
Give the intermediate CA constraints before signing it. CA:TRUE and keyCertSign identify a certificate that may sign other certificates. pathlen:0 allows it to issue leaf certificates but prevents it from issuing another subordinate CA.
# intermediate.ext
basicConstraints=critical,CA:TRUE,pathlen:0
keyUsage=critical,keyCertSign,cRLSign
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
openssl x509 -req -in intermediate.csr -CA root.crt -CAkey root.key \
-CAcreateserial -days 2 -sha256 \
-extfile intermediate.ext -out intermediate.crt
Issue a leaf that has only a service identity
The leaf must not be a CA. The extension file below restricts it to server authentication and adds the DNS name that an HTTPS client would compare to the requested hostname. The keyEncipherment setting is included for broadly compatible RSA server-certificate examples; confirm the key usage policy required by the actual TLS stack and certificate profile.
# leaf.ext
basicConstraints=critical,CA:FALSE
keyUsage=critical,digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=DNS:service.example.test
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
Generate and sign the leaf:
openssl req -newkey rsa:2048 -nodes -sha256 \
-subj '/CN=service.example.test' \
-keyout leaf.key -out leaf.csr
openssl x509 -req -in leaf.csr -CA intermediate.crt -CAkey intermediate.key \
-CAcreateserial -days 2 -sha256 \
-extfile leaf.ext -out leaf.crt
Before testing the chain, inspect the issuer and subject fields. They should show the intended progression: the root is self-issued, the intermediate is issued by the root, and the leaf is issued by the intermediate.
openssl x509 -in root.crt -noout -subject -issuer
openssl x509 -in intermediate.crt -noout -subject -issuer
openssl x509 -in leaf.crt -noout -subject -issuer
These fields are a quick diagnostic, not a complete verification. Matching text alone does not prove signatures, certificate constraints, expiry, or the chain-building result. The next commands make OpenSSL evaluate those conditions.
First prove that the missing intermediate is rejected
Point OpenSSL at the root trust anchor but deliberately omit the intermediate. This represents a common deployment mistake: a verifier has the root but the leaf’s issuer was not included in the service chain.
openssl verify -CAfile root.crt leaf.crt
printf 'exit status: %s\n' "$?"
The local test produced this result:
CN=service.example.test
error 20 at 0 depth lookup: unable to get local issuer certificate
error leaf.crt: verification failed
exit status: 2
The nonzero status is the important part for a gate. Error wording and exit values can vary with the OpenSSL version and the actual failure, so capture the version in the build log and fail the pipeline on any unexpected nonzero result. Do not add || true merely because the output is noisy. A missing intermediate is not fixed by trusting the leaf directly; that bypasses the issuer relationship the verifier is supposed to establish.
This check does not contact a server or test hostname selection. It verifies a certificate path from the leaf to the supplied trust anchor. Test a running HTTPS endpoint separately with its expected DNS name, protocol, client policy, and authorized network path.
Supply the intermediate as chain material
Now give the same verifier the intermediate as an untrusted linking certificate. Keep the root in -CAfile; that preserves the intended trust boundary.
openssl verify -CAfile root.crt \
-untrusted intermediate.crt leaf.crt
printf 'exit status: %s\n' "$?"
On the tested OpenSSL 3.5.6 installation, the previously failing leaf succeeded:
leaf.crt: OK
exit status: 0
This paired result is more useful than a positive check alone. It demonstrates that the command distinguishes the deployment error from the complete chain. In a service rollout, adapt the paths to the exact artifact format your proxy, application server, ingress controller, or secret consumer expects. Some products require a PEM file containing the leaf followed by intermediates; others accept a separate chain file. Read that product’s documentation rather than assuming the -untrusted command-line layout is its runtime configuration syntax.
Keep the root out of a server-presented chain unless the service documentation specifically requires it. Clients should already have their own trust anchors. Sending an unnecessary root can hide packaging mistakes and makes the artifact less clear to review.
After the test, remove the generated keys, certificate-signing requests, serial files, and certificates from the temporary directory. For a real deployment review, retain only redacted command output, the OpenSSL version, certificate fingerprints approved for the change record, and the final pass/fail result. Never publish private keys or full internal certificate subjects.