エラーメッセージ
The remote certificate was rejected by the provided RemoteCertificateValidationCallback.
.NET Core および .NET 5〜10 で共通の文言です。HttpClient はこれを「The SSL connection could not be established, see inner exception.」でラップします。コールバックに渡される SslPolicyErrors の値を見れば、.NET 自体も問題を検出していたかどうかがわかります。
.NET SslStream / HttpClient — a custom validation callback returned false
このエラーの意味
アプリケーションが独自の証明書検証コールバック(ServerCertificateCustomValidationCallback または RemoteCertificateValidationCallback)を設定しており、そのコールバックが false を返しました。.NET の組み込みのチェックは通過していた可能性があり、証明書を拒否すると判断したのはアプリケーションのコードです。サーバーの証明書が更新またはローテーションされた後に、証明書や鍵のピンニングで起こりがちです。
発生する理由
ピンニング対象の証明書が差し替えられた
よくある証明書のサムプリントをピンニングすると、更新のたびに失敗します(パブリック CA では現在、数か月ごとに更新されます)。新しい証明書はハッシュが変わるためです。
コールバック自身のロジックが有効な証明書を拒否している
よくあるもう一致しなくなった発行者名、サブジェクト、サムプリントのリストとの完全一致チェックや、エラーを示す errors 引数を無視する条件が原因です。
コールバックが実際の問題を正しく拒否している
ときどきerrors == SslPolicyErrors.None を返すコールバックは、信頼されていない証明書、期限切れの証明書、名前が一致しない証明書を拒否します。この場合、原因は証明書そのものにあります。
修正方法
- 1.
コールバックが受け取る内容をログに記録する
コールバックの中で SslPolicyErrors、サブジェクト、SHA-256 サムプリント、有効期限を記録し、そのうえでピンのリストを更新するか、証明書を修正してください。
csharp ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => { var thumbprint = cert!.GetCertHashString(HashAlgorithmName.SHA256); logger.LogWarning("TLS {Host}: errors={Errors} subject={Subject} sha256={Thumb} notAfter={NotAfter:u}", request.RequestUri?.Host, errors, cert.Subject, thumbprint, cert.NotAfter); return errors == SslPolicyErrors.None && pinnedThumbprints.Contains(thumbprint); }, - 2.
公開鍵をピンニングし、バックアップのピンも用意する
SPKI のピンは、鍵を再利用する更新であれば有効なままです。ローテーションでクライアントが接続できなくならないよう、必ず少なくとも 1 つのバックアップ鍵もピンニングしてください。
bash # Pin the public key (SPKI) rather than the certificate: it survives renewals with the same key openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null \ | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64