Сообщение об ошибке
invalid peer certificate: UnknownIssuer
Также выводится как
- InvalidCertificate(UnknownIssuer)
reqwest выводит эту ошибку в конце цепочки причин: «error sending request for url (…): client error (Connect): invalid peer certificate: UnknownIssuer»; в выводе Debug она выглядит как InvalidCertificate(UnknownIssuer). ureq выводит «io: invalid peer certificate: UnknownIssuer». Нативные исполняемые файлы на основе rustls (pnpm 12, многие CLI-утилиты) показывают тот же текст.
rustls (via reqwest, ureq, hyper-rustls, tonic) — rustls::Error::InvalidCertificate(CertificateError::UnknownIssuer)
Что это означает
rustls не смог построить цепочку от сертификата сервера ни к одному из настроенных якорей доверия. UnknownIssuer — единый код rustls для любой ошибки построения цепочки: частный CA или CA прокси, отсутствующий промежуточный сертификат или самоподписанный сертификат. Какие корневые сертификаты считаются доверенными, зависит от того, как собран крейт: с платформенным модулем проверки (хранилище ОС), с rustls-native-certs (корневые сертификаты ОС, загружаемые при запуске) или с webpki-roots (встроенный при компиляции список Mozilla).
Почему это происходит
Корпоративный или частный CA
частоЦепочка сервера заканчивается корневым сертификатом, которого нет среди настроенных якорей доверия; это типично для внутренних сервисов и прокси с инспекцией TLS.
Клиент доверяет только встроенным при компиляции публичным корневым сертификатам
частоКрейты, собранные с webpki-roots, полностью игнорируют хранилище ОС, поэтому корневой сертификат, установленный ИТ-отделом, на них никогда не действует. А платформенный модуль проверки, который reqwest 0.13 использует по умолчанию, хранилище ОС учитывает (в воспроизведении это подтвердилось после запуска update-ca-certificates).
Сервер не отправляет промежуточный сертификат
иногдаrustls не загружает недостающие промежуточные сертификаты; с сервером, который отдаёт только конечный сертификат, проверка завершается ошибкой UnknownIssuer, даже если корневой сертификат доверенный.
Самоподписанный сертификат сервера
иногдаСамоподписанные сертификаты rustls тоже относит к UnknownIssuer; добавьте такой сертификат в доверенные корневые для этого клиента или замените его.
Как исправить
- 1.
Добавьте корневой сертификат в клиент
ClientBuilder::add_root_certificate добавляет якорь доверия, сохраняя полную проверку (имён, дат).
rust // Trust a private root for this client (in addition to the default roots) let pem = std::fs::read("corp-root-ca.pem")?; let client = reqwest::Client::builder() .add_root_certificate(reqwest::Certificate::from_pem(&pem)?) .build()?; - 2.
Установите корневой сертификат в хранилище ОС
С платформенным модулем проверки (по умолчанию в reqwest 0.13) корневые сертификаты из хранилища операционной системы считаются доверенными; в Linux достаточно update-ca-certificates.
bash # reqwest 0.13 verifies with the platform verifier: install the root in the OS store sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt sudo update-ca-certificates - 3.
Проверьте, какой источник корневых сертификатов встроен в сборку
cargo tree показывает, что используется: webpki-roots или rustls-native-certs; если должно действовать хранилище ОС, переключите features.
bash # Which root source is compiled into this binary? (a crate that is absent reports no match) cargo tree -e features -i rustls-platform-verifier # OS verifier (reqwest 0.13 default) cargo tree -e features -i rustls-native-certs # OS roots loaded at start-up cargo tree -e features -i webpki-roots # Mozilla roots compiled in, OS store ignored - 4.
Владельцам серверов: отправляйте промежуточный сертификат
Если в рукопожатии только один сертификат и его издатель — промежуточный CA, конфигурация сервера неполна.
bash openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \ | grep -E '^ *[0-9]+ s:|^ *i:'
Связанные ошибки
invalid peer certificate: certificate expired: verification timerustls 0.23 (via reqwest, ureq, hyper-rustls)invalid peer certificate: certificate not valid for name "api.example.com"; cer…rustls 0.23 (via reqwest, ureq, hyper-rustls)Error: ERR_PNPM_RESOLVING_NPM_RESOLVER_NETWORK_ERRORpnpm 12 (native binary, rustls)x509: certificate signed by unknown authorityGo crypto/x509