エラーメッセージ
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、中間証明書の欠落、自己署名証明書のいずれでもこのコードになります。どのルートが信頼されるかは、クレートのビルド方法によって決まります。プラットフォーム検証器(OS のストア)、rustls-native-certs(起動時に読み込まれる OS のルート)、webpki-roots(コンパイル時に組み込まれる Mozilla のリスト)のいずれかです。
発生する理由
社内 CA またはプライベート CA
よくあるサーバーのチェーンが、設定されたトラストアンカーに含まれないルートで終わっています。社内サービスや TLS を検査するプロキシで典型的なケースです。
クライアントが、コンパイル時に組み込まれた公開ルートしか信頼していない
よくあるwebpki-roots を使ってビルドされたクレートは OS のストアを完全に無視するため、IT 部門がインストールしたルートは決して適用されません。一方、reqwest 0.13 のデフォルトのプラットフォーム検証器は OS のストアを使います(再現テストでも、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.
ルートを OS のストアにインストールする
プラットフォーム検証器(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 のどちらが使われているかがわかります。OS のストアを適用する必要がある場合は、フィーチャーを切り替えてください。
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.
サーバー管理者:中間証明書を送信する
ハンドシェイクに含まれる証明書が 1 つだけで、その発行者が中間 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