编程语言
.NET:The remote certificate is invalid according to the validation procedure
System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.The remote certificate is invalid according to the validation procedure 的意思是:.NET 的 SslStream 在 TLS 握手时拒绝了服务器证书。 HttpClient 通常把它包在 HttpRequestException: The SSL connection could not be established, see inner exception 里面。具体原因在 SslPolicyErrors 标志里:证书链不受信任或不完整、域名不匹配,或者没拿到证书。
较新的 .NET 版本常常会把原因直接附在消息后面,比如 ...: RemoteCertificateNameMismatch, RemoteCertificateChainErrors,或 The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot。
快速修复
修法取决于 SslPolicyErrors 里是哪个标志。先从一台干净的机器检查服务器:
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
再按下表找到对应修法:
| SslPolicyErrors / 链状态 | 含义 | 修法 |
|---|---|---|
RemoteCertificateChainErrors + PartialChain |
服务器没发中间证书 | 服务器发送完整证书链 |
RemoteCertificateChainErrors + UntrustedRoot |
本机不信任根证书 | 安装私有根或代理根证书 |
RemoteCertificateChainErrors + NotTimeValid |
证书过期或尚未生效 | 续期;检查客户端时间 |
RemoteCertificateNameMismatch |
访问的主机不在证书 SAN 里 | 换成正确的域名或重签证书 |
RemoteCertificateNotAvailable |
服务器没出示证书 | 检查服务器 TLS 配置 |
公网域名可以直接用 SSL 检测工具一次看清证书链、域名和有效期。
原因
这个异常有 4 类原因,最常见的是根证书不受信任,私有 CA、自签名证书和企业代理都属于这一类。
1. 根证书不受信任(私有 CA、自签名、企业代理)
内部接口、解密 HTTPS 的企业代理、本地开发服务器用的根证书都不在公共证书库里。
- 确认: 链状态是
UntrustedRoot,最上层签发者是公司名或代理设备。 - 修复: 把根证书装进系统信任库(见下文)。如果只想让某一个应用信任它,就显式按这张根证书校验:
var companyRoot = X509Certificate2.CreateFromPemFile("company-root.pem");
var handler = new SocketsHttpHandler();
handler.SslOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) =>
{
if (errors == SslPolicyErrors.None) return true;
if (errors != SslPolicyErrors.RemoteCertificateChainErrors || cert is not X509Certificate2 leaf)
return false; // 域名不匹配绝不放行
using var custom = new X509Chain();
custom.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
custom.ChainPolicy.CustomTrustStore.Add(companyRoot);
return custom.Build(leaf);
};
var client = new HttpClient(handler);
这样仍然校验域名,只额外信任一张指定的根证书。
2. 服务器证书链不完整
- 确认: 链状态是
PartialChain;openssl s_client只显示一张证书。 - 修复: 在服务器或负载均衡上配置站点证书加中间证书。IIS 要把中间证书导入计算机账户的“中级证书颁发机构”。详见缺少中间证书。
3. 域名不匹配
- 确认:
RemoteCertificateNameMismatch;你用 IP、内部别名或证书没覆盖的主机名访问。 - 修复: 改用证书 SAN 里列出的名字,或重新签发。
4. 证书过期或系统时间不对
- 确认:
NotTimeValid。同时检查服务器证书到期日和客户端时间,从快照恢复的虚拟机时间经常不准。
各环境修法
.NET 在 Windows 上读 Windows 证书库,在 Linux 上读 OpenSSL 的 CA 证书包,在 macOS 上读系统钥匙串。私有根证书要装进当前平台对应的位置。
Windows(.NET Framework 和 .NET): 把根证书导入“本地计算机”的“受信任的根证书颁发机构”(certlm.msc),或用 PowerShell:
Import-Certificate -FilePath .\company-root.cer -CertStoreLocation Cert:\LocalMachine\Root
Linux 和容器: .NET 读 OpenSSL 的信任库。
FROM mcr.microsoft.com/dotnet/aspnet:8.0
COPY company-root.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates
Alpine 版镜像先用 apk 安装 ca-certificates。国内拉取 mcr.microsoft.com 镜像较慢时,可以在公司的镜像仓库里做一份缓存,但同样要在构建时加入根证书。
macOS: 把根证书加入“系统”钥匙串并设为“始终信任”。
ASP.NET Core 本地开发: 在 Windows 和 macOS 上执行 dotnet dev-certs https --trust 信任开发证书。
.NET Framework 4.x: 如果内层消息说连接被意外关闭,可能是程序只提供了老旧的 TLS 版本,那是协议问题,不是证书校验失败,见 The SSL connection could not be established。
不要这样做
下面 3 种写法都能让异常消失,但会让任何人都能冒充你的服务器。
- 在 .NET Framework 里写
ServicePointManager.ServerCertificateValidationCallback = (s, c, ch, e) => true;。 这会关闭整个进程所有连接的证书校验。 - 在隔离测试之外使用
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator。 - 在回调里忽略
RemoteCertificateNameMismatch。 信任一张根证书是合理的,接受任意域名则不然:那张根签发的任何证书都能冒充你的服务器。
常见问题
怎么知道具体是哪项校验失败?
在校验回调里记录 SslPolicyErrors 的值;如果是证书链错误,再逐条记录 chain.ChainStatus,回调本身仍然返回 errors == SslPolicyErrors.None。UntrustedRoot、PartialChain、NotTimeValid、Revoked 等状态会直接指出问题。
只有在 Linux 的 Docker 容器里报错,为什么?
在 Linux 上,.NET 用的是容器里 OpenSSL 的信任库,而不是宿主机或 Windows 的证书库。在镜像里安装 ca-certificates,用 update-ca-certificates 加入私有根证书,然后重新构建镜像。
ASP.NET Core 开发证书不受信任,该执行什么?
在 Windows 或 macOS 上执行 dotnet dev-certs https --trust。Linux 上的信任方式取决于发行版和 .NET 版本,按 Microsoft Learn 上“Enforce HTTPS in ASP.NET Core”一文的步骤操作。
测试里能用 DangerousAcceptAnyServerCertificateValidator 吗?
只能用于测试进程内部的本地测试服务器。名字里的 Dangerous 就是警告:它对任何主机接受任何证书。绝不能让它出现在生产代码能读到的配置里。