Command-line tools

OpenSSL error 20: unable to get local issuer certificate

By · Last verified

Error messageerror 20 at 0 depth lookup: unable to get local issuer certificate

Error 20 means OpenSSL could not find the certificate that issued one of the certificates in the chain. The depth in the message tells you which link is missing: depth 0 means the leaf's issuer (an intermediate) was not supplied; a higher depth means the chain stops at a CA that is not in your trust store.

Quick fix

The depth in the error shows which certificate's issuer is missing.

Test the server and look at the depth and the chain it sends:

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>&1 \
  | grep -E "depth=|verify error|^ *[0-9] s:|Verify return"

For local files, give OpenSSL the intermediates and the root explicitly:

openssl verify -CAfile root.pem -untrusted intermediates.pem leaf.pem
  • s_client shows one certificate and error 20 at depth 0: the server must send its intermediate; see missing intermediate certificate.
  • Error 20 at depth 1 or 2: the top certificate is not in your trust store; add the right root or update the CA bundle.

Causes

A missing intermediate shows up at depth 0; an untrusted root shows up at a higher depth.

1. The intermediate was not supplied (depth 0)

  • Confirm: only one s: line in the s_client output; or with files, openssl verify leaf.pem fails but openssl verify -untrusted intermediates.pem leaf.pem succeeds.
  • Fix: on the server, deploy leaf plus intermediates (fullchain.pem). With local files, always pass the chain via -untrusted. The chain builder finds the correct intermediate for a leaf.

2. The root is not trusted (depth ≥ 1)

The chain is complete but ends at a CA your store does not contain: a private CA, a TLS-inspecting proxy, or a new public root on an old system.

  • Confirm: the last i: line in -showcerts names a CA that is not in /etc/ssl/certs/ (or your bundle).
  • Fix: add the private root to the system store, or pass it with -CAfile for the test. Update ca-certificates for public roots.

3. Wrong trust store in use

A custom -CAfile or SSL_CERT_FILE replaces the default anchors.

  • Confirm: the same command works without your -CAfile or without the environment variable.
  • Fix: use a bundle that contains both the public roots you need and any private ones.

4. Testing with the wrong file

Verifying an intermediate or a CA bundle file instead of the leaf, or a leaf from a different chain, also produces error 20.

  • Confirm: openssl x509 -in file.pem -noout -subject -issuer shows something other than your server certificate.
  • Fix: split the bundle and verify the leaf.

Fixes by environment

At depth 0, install the intermediate on the server; at a higher depth, add the root to the trust store your OpenSSL build reads.

Debian / Ubuntu: private roots go in /usr/local/share/ca-certificates/*.crt, then sudo update-ca-certificates.

RHEL family: /etc/pki/ca-trust/source/anchors/, then sudo update-ca-trust.

macOS: the LibreSSL openssl shipped with macOS and a Homebrew OpenSSL use different CA files; check openssl version -d for the directory each one uses.

Windows: OpenSSL for Windows does not read the Windows certificate store; pass -CAfile with a PEM bundle exported from the store or downloaded from curl.se.

Interpreting other tools: curl error 60, Python CERTIFICATE_VERIFY_FAILED and Node UNABLE_TO_GET_ISSUER_CERT_LOCALLY are the same OpenSSL error 20 surfaced by those programs; see curl error 60.

Don't do this

  • Ignore error 20 in s_client because the connection still works. s_client continues after verification errors by default; real clients do not. Use -verify_return_error to make it fail like they do.
  • Add the intermediate as a trusted root to silence the error. Trust anchors should be roots; trusting an intermediate hides a broken server chain from you but not from your users.
  • Put the leaf certificate into the trust store. It "works" until the next renewal and then breaks again.

FAQ

Why does `openssl verify cert.pem` fail for a certificate that works in browsers?

openssl verify only uses the first certificate in the file and the trust store. It does not treat other certificates in the same file as intermediates. Run openssl verify -untrusted chain.pem cert.pem (with the intermediates in chain.pem) to test the full chain.

What is the difference between error 20 and error 21?

Error 20 reports that the issuer could not be found locally. Error 21 ("unable to verify the first certificate") is what s_client typically shows when the server sent only one certificate and its issuer is unknown. Both usually mean a missing intermediate.

Does s_client use the system CA store?

Yes, by default it loads OpenSSL's default verify locations, which on most Linux distributions is the system bundle. -CAfile adds or overrides the trust anchors; -no-CAfile and -no-CApath disable the defaults.

Sources

  1. OpenSSL openssl-verify
  2. OpenSSL X509_STORE_CTX_get_error (verification error codes)
  3. OpenSSL openssl-verification-options
  4. OpenSSL openssl-s_client