错误信息
The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot
也可能输出为
- System.Security.Authentication.AuthenticationException: The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot
冒号后面的标志来自 X509ChainStatusFlags,可以组合出现(例如“UntrustedRoot, RevocationStatusUnknown”)。自签名的服务器证书同样报告 UntrustedRoot。在 Linux 上,.NET 使用 OpenSSL 和发行版的 CA 存储;在 Windows 和 macOS 上,则使用系统证书存储。
.NET SslStream / HttpClient — AuthenticationException (X509ChainStatusFlags.UntrustedRoot)
含义
.NET 将服务器的证书链构建到了某个根证书,但平台的证书存储不信任该根证书(或者证书链终止于一张不受信任的自签名证书)。证书本身可能没有问题;只是这台机器不信任它背后的证书颁发机构。HttpClient 会把它报告为“The SSL connection could not be established”的内部异常。
原因
公司 CA 或私有 CA
常见内部 API、使用域 CA 证书的 SQL Server,或者实验环境中的服务,使用的根证书在这台机器(通常是 Linux 容器)上并不存在。
执行 TLS 检查的代理
常见被重新签名的连接,其证书链指向代理的根证书。Windows 机器通常已通过组策略获得该根证书,Linux 容器和 WSL 则没有。
自签名的服务器证书
常见自签名证书(包括开发服务器的证书)的证书链终止于其自身,会被报告为 UntrustedRoot。
容器镜像中缺少根证书
偶见安装在宿主机或 Windows 中的根证书,并不在运行 .NET 应用的 Linux 容器里。
修复方法
- 1.
Linux:将根证书安装到系统证书存储
Linux 上的 .NET 读取 OpenSSL 的证书存储,因此运行 update-ca-certificates(或发行版中的等效命令)就够了;在容器中,要在 Dockerfile 里完成这一步。
bash # Linux (incl. containers): .NET uses the OpenSSL system store sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt sudo update-ca-certificates - 2.
Windows 和 macOS:在系统证书存储中信任根证书
在 Windows 上将根证书导入 LocalMachine\Root,在 macOS 上导入“系统”钥匙串;.NET 使用的就是这些存储。
text # Windows (elevated PowerShell): trust the root machine-wide Import-Certificate -FilePath .\corp-root-ca.cer -CertStoreLocation Cert:\LocalMachine\Root # macOS sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain corp-root-ca.pem - 3.
只为一个客户端信任私有根证书
将 SocketsHttpHandler.SslOptions.CertificateChainPolicy 与 CustomRootTrust 配合使用,可以保留完整的验证,只是以你的根证书作为信任锚。自定义策略默认启用在线吊销检查,因此要根据你的 CA 实际发布的内容设置 RevocationMode(在我们的测试中,没有 CRL/OCSP 的私有证书会因 RevocationStatusUnknown 而失败,直到设置为 NoCheck 为止)。
csharp // Trust one private root for this client only; chain, dates and name are still verified. var root = X509CertificateLoader.LoadCertificateFromFile("corp-root-ca.pem"); var handler = new SocketsHttpHandler(); handler.SslOptions.CertificateChainPolicy = new X509ChainPolicy { TrustMode = X509ChainTrustMode.CustomRootTrust, // a custom policy defaults to online revocation checks; use NoCheck only if the CA publishes no CRL/OCSP RevocationMode = X509RevocationMode.NoCheck, }; handler.SslOptions.CertificateChainPolicy.CustomTrustStore.Add(root); using var client = new HttpClient(handler); - 4.
开发环境:信任 ASP.NET Core 开发证书
在 Windows 和 macOS 上,dotnet dev-certs https --trust 会安装并信任 localhost 开发证书(在 Linux 上,如何信任它取决于发行版)。
bash # ASP.NET Core development certificate on localhost dotnet dev-certs https --trust
相关错误
The SSL connection could not be established, see inner exception..NET HttpClientThe remote certificate is invalid because of errors in the certificate chain: P….NET SslStream / HttpClientThe remote certificate was rejected by the provided RemoteCertificateValidation….NET SslStream / HttpClientcurl: (60) SSL certificate problem: self-signed certificate in certificate chaincurl (OpenSSL backend)