Сообщение об ошибке
verify error:num=20:unable to get local issuer certificate
Также выводится как
- Verify return code: 20 (unable to get local issuer certificate)
- error 20 at 0 depth lookup: unable to get local issuer certificate
s_client выводит «verify error:num=20:…» во время рукопожатия и «Verify return code: 20 (…)» в итоговой сводке; openssl verify выводит «error 20 at <depth> depth lookup: …». По умолчанию s_client продолжает работу после ошибок проверки; чтобы он завершался с ошибкой, добавьте -verify_return_error.
OpenSSL CLI (openssl s_client / openssl verify) — X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
Что это означает
Ошибка OpenSSL 20 (X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY): строя цепочку, OpenSSL дошёл до сертификата, издателя которого не нашёл ни среди сертификатов, присланных сервером, ни в хранилище доверенных сертификатов. Строка depth, выведенная прямо перед ошибкой, называет сертификат, издателя которого не хватает. Тот же код стоит за ошибкой curl (60), CERTIFICATE_VERIFY_FAILED в Python и UNABLE_TO_GET_ISSUER_CERT_LOCALLY в Node.js.
Почему это происходит
Цепочка заканчивается корневым сертификатом, которого нет в хранилище
частоЧастный или корпоративный корневой сертификат (или прокси с инспекцией TLS, который отправляет только свой промежуточный сертификат). Строка depth с наибольшим номером называет промежуточный сертификат, издателя которого у вас нет.
Сервер не отправляет свой промежуточный сертификат
частоВыводится только depth=0, а издатель этого сертификата — промежуточный CA. OpenSSL никогда не скачивает недостающие промежуточные сертификаты, поэтому сервер должен отправлять их сам.
При проверке не использовалось хранилище доверенных сертификатов
иногдаУ openssl verify с одним-единственным сертификатом или у s_client в образе без ca-certificates нет корневых сертификатов, которыми можно завершить цепочку, даже для публичных сайтов.
Как исправить
- 1.
Посмотрите строки depth
Глубина 0 — сертификат сервера, номера больше — присланные им промежуточные сертификаты. Последняя строка depth перед ошибкой указывает на сертификат, издателя которого не хватает.
bash echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>&1 | grep -E '^depth|verify' # depth=1 CN=Example Corp Issuing CA <- the highest certificate received # verify error:num=20:... <- its issuer is not in the trust store - 2.
Проверьте с ожидаемым корневым сертификатом
-CAfile с правильным корневым сертификатом (и -verify_return_error, чтобы сбои прерывали работу) покажет, завершает ли этот корневой сертификат цепочку; «Verify return code: 0 (ok)» означает, что завершает.
bash # Verify against a specific root (exit code and "Verify return code: 0 (ok)" confirm it) echo | openssl s_client -connect api.example.com:443 -servername api.example.com \ -CAfile corp-root-ca.pem -verify_return_error 2>&1 | grep 'Verify return code' - 3.
Проверьте файлы сертификатов офлайн
Передайте openssl verify корневой сертификат через -CAfile, а промежуточные — через -untrusted; один конечный сертификат проверить нельзя.
bash # Offline: root as trust anchor, intermediate(s) as untrusted helpers openssl verify -CAfile corp-root-ca.pem -untrusted intermediate.pem server.pem - 4.
Установите корневой сертификат в системное хранилище
Если цепочка правомерно заканчивается корневым сертификатом компании, добавьте его в хранилище дистрибутива; тогда инструменты на базе OpenSSL на этой машине будут ему доверять.
bash sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt sudo update-ca-certificates # openssl reads /etc/ssl/certs by default on Debian/Ubuntu
Связанные ошибки
verify error:num=21:unable to verify the first certificateOpenSSL CLI (openssl s_client)verify error:num=19:self-signed certificate in certificate chainOpenSSL CLI (openssl s_client / openssl verify)curl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local…Python ssl module (OpenSSL)