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.

In this article
- Входные условия
- Какую цепочку строит OpenSSL
- Ошибка без промежуточного сертификата
- Полная цепочка через -untrusted
- Почему intermediate не стоит молча делать корнем доверия
- Как читать depth и номер ошибки
- Имя хоста проверяется отдельно
- Протокол воспроизводимого теста
- Порядок проверки рабочей цепочки
- Границы примера
- Prerequisites
- Which chain does OpenSSL build
- Error Without an Intermediate Certificate
- Full Chain via -untrusted
- Why You Should Not Silently Make the Intermediate a Trust Anchor
- How to read depth and error number
- The host name is checked separately
- Protocol of the reproducible test
- Verification order for the working chain
- Example boundaries
Если leaf-сертификат подписан промежуточным CA, одного доверенного root.pem недостаточно: OpenSSL должен получить intermediate.pem для построения пути. Передайте корневой сертификат через -CAfile, а промежуточный — через -untrusted. Затем проверяйте не только строку OK, но и код завершения процесса.
Параметр -untrusted здесь не объявляет сертификат вредоносным. Он означает, что сертификат можно использовать для построения цепочки, но нельзя считать самостоятельной точкой доверия. Это различие помогает не лечить ошибку 20 случайным добавлением сертификатов в системное хранилище.
Входные условия
Ubuntu 24.04.3 LTS и OpenSSL 3.0.13.
Три PEM-файла учебной цепочки: leaf.pem, intermediate.pem и root.pem.
Leaf выдан для DNS-имени backup.example.test; приватные ключи для проверки цепочки не нужны.
Команды выполняются в изолированном временном каталоге без сети и рабочих сертификатов.
Проверка воспроизведена 22 октября 2026 года.
Какую цепочку строит OpenSSL
Документация openssl verify для версии 3.0 определяет -untrusted как набор сертификатов для построения цепочки, а -show_chain показывает построенный путь и помечает сертификаты из этого набора как untrusted.
В терминах RFC 5280 путь связывает проверяемый сертификат с trust anchor. В учебной модели это leaf → intermediate → root. Валидатор проверяет подписи, сроки и ограничения сертификатов, а приложение может добавлять отдельные требования.

Ошибка без промежуточного сертификата
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
Команда завершилась с кодом 2. Root доверен, но leaf подписан не им напрямую. Без intermediate.pem валидатор не может перейти от depth 0 к следующему издателю. По одному error 20 рано делать вывод, что root неверный: сначала нужно понять, какого сертификата не хватает на указанной глубине.
Полная цепочка через -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
Результат — код 0. Пометка untrusted у leaf и intermediate в выводе -show_chain не означает ошибку: эти сертификаты участвовали в построении пути, но доверие завершилось на root из -CAfile.
Почему intermediate не стоит молча делать корнем доверия
Если передать intermediate.pem как единственный -CAfile без -partial_chain, учебная команда вернула error 2 на depth 1: издатель промежуточного сертификата не найден. С -partial_chain тот же intermediate становится допустимым завершением пути, и проверка проходит. Это уже другая политика доверия, а не ремонт недостающей цепочки.
Команда | Результат | Код | Что проверено |
|---|---|---|---|
root без intermediate | error 20 на depth 0 | 2 | не найден сертификат issuer для leaf |
root + -untrusted intermediate | OK, путь до root | 0 | полная учебная цепочка |
intermediate как -CAfile | error 2 на depth 1 | 2 | нет издателя intermediate |
intermediate + -partial_chain | OK до intermediate | 0 | intermediate принят как trust anchor |
неверный root + intermediate | error 20 на depth 1 | 2 | intermediate не связан с доверенным root |
Не добавляйте -partial_chain только ради зелёного результата. Используйте его лишь тогда, когда ваша политика действительно доверяет указанному промежуточному CA как конечной точке пути. Для обычной серверной цепочки сохраняйте разницу между trust anchor и сертификатами, которые сервер передаёт для построения пути.

Как читать depth и номер ошибки
Фрагмент | Смысл в учебной цепочке |
|---|---|
depth 0 | проверяемый leaf-сертификат |
depth 1 | промежуточный CA |
depth 2 | корневой CA |
error 20 | не найден локальный издатель на указанной глубине |
error 2 | не удалось получить сертификат издателя |
Глубина указывает место, где построение пути остановилось. В выполненном тесте без intermediate ошибка появилась на depth 0; с неверным root — на depth 1. Одинаковый номер ошибки может сопровождать разные конфигурационные причины, поэтому сохраняйте всю команду, глубину, subject и код завершения.
Имя хоста проверяется отдельно
Успешная цепочка ещё не подтверждает, что сертификат подходит нужному DNS-имени. Для учебного leaf с subjectAltName backup.example.test выполнены две отдельные проверки.
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
Совпавшее имя дало код 0, другое имя — error 62 и код 2. Поэтому отчёт «цепочка корректна» и отчёт «сертификат подходит имени» должны оставаться двумя явными проверками.
Протокол воспроизводимого теста
Root CA создан самоподписанным с CA:TRUE и keyCertSign.
Intermediate CA подписан root и ограничен pathlen:0.
Leaf подписан intermediate, содержит CA:FALSE, serverAuth и DNS:backup.example.test.
Без intermediate получены error 20 и exit 2.
С root и -untrusted intermediate получены OK и exit 0.
Intermediate как -CAfile без -partial_chain дал error 2 и exit 2; с -partial_chain — OK и exit 0.
Неверный root дал error 20 на depth 1 и exit 2.
Правильное имя прошло с exit 0; другое имя дало error 62 и exit 2.
Все ключи и сертификаты были созданы только внутри временного каталога и удалены после теста. Они не относятся к клиенту или домену рабочей системы. Пример проверяет поведение локального валидатора, а не настройку конкретного веб-сервера.
Порядок проверки рабочей цепочки
Зафиксируйте версию OpenSSL, проверяемый leaf, ожидаемое DNS-имя и доверенное корневое хранилище.
Получите intermediate из проверенного источника и не смешивайте его с приватным ключом.
Сначала выполните verify с явными -CAfile и -untrusted, затем сохраните -show_chain и код завершения.
Если ошибка указывает depth 0, проверьте наличие издателя leaf; если depth 1 — связь intermediate с выбранным root.
Отдельно выполните -verify_hostname или -verify_ip согласно типу адреса.
Не применяйте -partial_chain, -no_check_time и отключение проверок без документированного требования.
После локальной проверки проверьте фактический набор сертификатов, который отдаёт сервис, и повторите проверку тем TLS-клиентом, которым пользуетесь.
Для публичного сайта фактический сертификат, срок и переданную цепочку можно дополнительно проверить через встроенную SSL-проверку. Локальная модель и внешний ответ сервера решают разные задачи.
Границы примера
Тест не выполняет TLS-handshake, SNI и сетевой запрос.
Не проверяются OCSP, CRL и доступность URL отзыва.
Не исследуются порядок сертификатов в конфигурации конкретного nginx, Apache или панели.
Учебные сертификаты действуют три дня; это не рекомендация по срокам рабочих сертификатов.
Проверка цепочки не подтверждает соответствие leaf приватному ключу и не заменяет проверку прав на файлы.
Коды и тексты ошибок относятся к OpenSSL 3.0.13; в другой версии формулировка может отличаться.
Критерий готовности: команда с ожидаемым root и явным intermediate завершается кодом 0, -show_chain заканчивается на нужном trust anchor, а отдельная проверка имени также проходит. Любая ошибка требует разбора глубины и источника сертификата, а не случайного расширения доверенного хранилища.
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.

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.

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
Record the OpenSSL version, the leaf being verified, the expected DNS name, and the trusted root store.
Obtain the intermediate from a trusted source and do not mix it with the private key.
First run verify with explicit -CAfile and -untrusted, then save -show_chain and the exit code.
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.
Separately run -verify_hostname or -verify_ip depending on the address type.
Do not use -partial_chain, -no_check_time, or disable checks without a documented requirement.
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.