Programming languages

.NET: The remote certificate is invalid according to the validation procedure

By · Last verified

Error messageSystem.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.

The remote certificate is invalid according to the validation procedure means .NET's SslStream rejected the server's certificate during the TLS handshake. HttpClient usually wraps it in HttpRequestException: The SSL connection could not be established, see inner exception. The real reason is in the SslPolicyErrors flags: an untrusted or incomplete chain, a name mismatch, or no certificate at all.

Newer .NET versions often append the reason to the message, for example ...: RemoteCertificateNameMismatch, RemoteCertificateChainErrors or The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot.

Quick fix

The SslPolicyErrors flag tells you which fix applies.

Find out which flag is set. Check the server from a clean machine first:

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null

Then match the flag to the fix:

SslPolicyErrors / chain status Meaning Fix
RemoteCertificateChainErrors + PartialChain Server omitted an intermediate Send the full chain from the server
RemoteCertificateChainErrors + UntrustedRoot Root not trusted on this machine Install the private or proxy root
RemoteCertificateChainErrors + NotTimeValid Expired or not-yet-valid Renew; check the client clock
RemoteCertificateNameMismatch Host not in the certificate SAN Use the right host or reissue
RemoteCertificateNotAvailable No certificate presented Check the server's TLS setup

The SSL checker shows chain, names and dates for public hosts in one report.

Causes

1. Untrusted root (private CA, self-signed, proxy)

Internal APIs, corporate TLS-inspecting proxies and local development servers use roots that are not in the public store.

  • Confirm: chain status UntrustedRoot; the top issuer is your company or proxy.
  • Fix: install the root into the machine trust store (below). If only one application should trust it, validate against that root explicitly:
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;                       // never ignore name mismatches
    using var custom = new X509Chain();
    custom.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
    custom.ChainPolicy.CustomTrustStore.Add(companyRoot);
    return custom.Build(leaf);
};
var client = new HttpClient(handler);

This still enforces host name checks and only adds one specific root.

2. Incomplete chain on the server

  • Confirm: chain status PartialChain; openssl s_client shows a single certificate.
  • Fix: configure leaf plus intermediates on the server or load balancer. In IIS, import the intermediate into the computer's "Intermediate Certification Authorities" store. See missing intermediate certificate.

3. Name mismatch

  • Confirm: RemoteCertificateNameMismatch; you connect by IP, by an internal alias, or through a host header the certificate does not cover.
  • Fix: connect with a name listed in the certificate's SAN, or reissue it.

4. Expired certificate or wrong clock

  • Confirm: NotTimeValid. Check the server's expiry and the client's clock, which matters for VMs restored from snapshots.

Fixes by environment

On every OS, the fix is to trust the right root in the store .NET reads, or to fix the server's chain or name.

Windows (.NET Framework and .NET): import the root into "Trusted Root Certification Authorities" for the Local Computer (certlm.msc) or with PowerShell:

Import-Certificate -FilePath .\company-root.cer -CertStoreLocation Cert:\LocalMachine\Root

Linux and containers: .NET reads the OpenSSL store.

FROM mcr.microsoft.com/dotnet/aspnet:8.0
COPY company-root.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates

For Alpine-based images install ca-certificates with apk first.

macOS: add the root to the System keychain and mark it "Always Trust".

ASP.NET Core local development: dotnet dev-certs https --trust trusts the development certificate on Windows and macOS.

.NET Framework 4.x: if the inner message instead says the connection was closed unexpectedly, the app may be offering only old TLS versions; that is a protocol problem, not this error.

Don't do this

  • ServicePointManager.ServerCertificateValidationCallback = (s, c, ch, e) => true; in .NET Framework. It disables validation for every connection in the process.
  • HttpClientHandler.DangerousAcceptAnyServerCertificateValidator outside isolated tests.
  • Callbacks that ignore RemoteCertificateNameMismatch. Trusting a root is reasonable; accepting any name is not, because any certificate from that root could impersonate your server.

FAQ

How do I find out which validation check failed?

Log the SslPolicyErrors value and, for chain errors, each chain.ChainStatus entry inside a validation callback that still returns errors == SslPolicyErrors.None. Statuses such as UntrustedRoot, PartialChain, NotTimeValid or Revoked name the exact problem.

It fails only in Docker on Linux. Why?

On Linux, .NET validates against the OpenSSL trust store inside the container, not the host's or Windows' store. Install ca-certificates, add your private root with update-ca-certificates, and rebuild the image.

The ASP.NET Core development certificate is not trusted. What should I run?

Run dotnet dev-certs https --trust on Windows or macOS. Linux trust handling depends on the distribution and .NET version; follow the Microsoft Learn page on enforcing HTTPS for the exact steps.

Is DangerousAcceptAnyServerCertificateValidator acceptable in tests?

Only against a local test server inside the test process. Its name is a warning: it accepts any certificate for any host. Never let it reach configuration that production code can load.

Sources

  1. SslPolicyErrors Enum (Microsoft Learn)
  2. RemoteCertificateValidationCallback Delegate (Microsoft Learn)
  3. Enforce HTTPS in ASP.NET Core (Microsoft Learn)
  4. Generate self-signed certificates with the .NET CLI (Microsoft Learn)