Mensaje de error
The remote certificate was rejected by the provided RemoteCertificateValidationCallback.
El texto es estable en .NET Core y .NET 5–10; HttpClient lo envuelve en «The SSL connection could not be established, see inner exception.» El valor de SslPolicyErrors que se pasa a la devolución de llamada te indica si el propio .NET también encontró un problema.
.NET SslStream / HttpClient — a custom validation callback returned false
Qué significa
La aplicación instaló su propia devolución de llamada de validación de certificados (ServerCertificateCustomValidationCallback o RemoteCertificateValidationCallback), y esa devolución de llamada devolvió false. Puede que las comprobaciones integradas de .NET se superaran; fue tu código el que decidió rechazar el certificado. Es típico con la fijación de certificados o de claves después de que se renueve o rote el certificado del servidor.
Por qué ocurre
Se sustituyó el certificado fijado
frecuenteLa fijación de la huella digital de un certificado se rompe en cada renovación (con las CA públicas, ahora cada pocos meses); el certificado nuevo tiene un hash nuevo.
La propia lógica de la devolución de llamada rechaza certificados válidos
frecuenteComprobaciones de un nombre de emisor exacto, de un sujeto o de una lista de huellas digitales que ya no coinciden, o condiciones que ignoran el argumento errors.
La devolución de llamada rechaza, con razón, un problema real
ocasionalUna devolución de llamada que devuelve errors == SslPolicyErrors.None rechaza los certificados que no son de confianza, que han caducado o cuyo nombre no coincide; en ese caso, la causa es el propio certificado.
Cómo solucionarlo
- 1.
Registrar lo que ve la devolución de llamada
Registra SslPolicyErrors, el sujeto, la huella digital SHA-256 y la caducidad dentro de la devolución de llamada; después, actualiza la lista de pines o corrige el certificado.
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.
Fijar la clave pública y mantener un pin de respaldo
Los pines SPKI siguen siendo válidos tras las renovaciones que reutilizan la clave; fija siempre al menos una clave de respaldo para que una rotación no deje fuera a los clientes.
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