Сообщение об ошибке
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
частоВнутренние API, SQL Server с сертификатом доменного CA или сервисы на тестовом стенде используют корневые сертификаты, которых нет на машине (часто это Linux-контейнер).
Прокси с инспекцией TLS
частоВ переподписанных соединениях цепочка ведёт к корневому сертификату прокси. На машинах с Windows он обычно есть благодаря групповой политике, а в Linux-контейнерах и WSL — нет.
Самоподписанный сертификат сервера
частоСамоподписанные сертификаты (в том числе у серверов разработки) сами завершают свою цепочку, и .NET помечает их как UntrustedRoot.
В образе контейнера нет корневого сертификата
иногдаКорневого сертификата, установленного на хосте или в Windows, нет внутри Linux-контейнера, где работает приложение .NET.
Как исправить
- 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: сделайте корневой сертификат доверенным в системном хранилище
Импортируйте корневой сертификат в LocalMachine\Root в Windows или в системную связку ключей (System) в 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 сохраняет полную проверку, но использует в качестве якоря доверия ваш корневой сертификат. Пользовательская политика по умолчанию включает онлайн-проверку отзыва, поэтому задайте RevocationMode в соответствии с тем, что публикует ваш CA (в нашей проверке частные сертификаты без 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 в доверенные
dotnet dev-certs https --trust устанавливает сертификат разработки для localhost и делает его доверенным в Windows и macOS (в 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)