Today one of our clients received a System.Net.WebException error on a newly deployed ASP.NET web application. Part of the exception was: "System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.". Here is how we resolved that issue.
System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
This System.Net.WebException obviously has something to do with an SSL/TLS secure connection and certificates.
The System.Net.WebException our client received was:
System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. --->
System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure. at
System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception) at
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult) at
System.Net.TlsStream.CallProcessAuthentication(Object state) at System.Threading.ExecutionContext.runTryCode(Object userData) at
System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData) at
System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at
System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at
System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result) at
System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size) at
System.Net.PooledStream.Write(Byte[] buffer, Int32 offset, Int32 size) at
System.Net.ConnectStream.WriteHeaders(Boolean async)
--- End of inner exception stack trace ---
at System.Net.WebClient.DownloadFile(Uri address, String fileName) at
System.Net.WebClient.DownloadFile(String address, String fileName) at
[...]
Before contacting us, they tried all on Google available options and possible solutions, like ignoring all SSL certificate errors, but to no avail.
Upon investigation, we quickly noticed three distinct, related and important issues:
- the remote site uses a Server Name Indication (SNI) certificate, installed on a different domain name
- the web application was published to a IIS 6.0 (Windows Server 2003) web server
- a System.Security.Authentication.AuthenticationException:
The remote certificate is invalid according to the validation procedure
. This error message is caused because the process is not able to validate the certificate supplied by the server during an HTTPS (SSL) request
Protip:
- Install SSL/TLS certificates in Windows Server using PowerShell
- Use PowerShell with SSL client certificates for HTTPS GET requests
IIS 6.0 + Server Name Indication (SNI) certificates = System.Net.WebException
A Server Name Indication (SNI) certificate basically means you can install one SSL/TLS certificate on a web server, to use on multiple domain names. The TLS part takes the negotiation, and that enables the server to select the correct virtual domain early and present the browser with the certificate containing the correct name.
Therefore with clients and servers that support SNI, a single IP address can be used to serve a group of domain names for which it is impractical to get a common certificate.
Windows Server 2003 (IIS 6.0), Windows Server 2008 (IIS 7.0) and Windows Server 2008 R2 (IIS 7.5) do not support SNI-certificates.
Resolve "System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel." exception in IIS, WCF and SharePoint
The fix for "System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel." exception in IIS is easy: Just move the website to IIS 8.0+
You might wonder what the solution to this error message was. Well, simple: Move the website to an IIS 8.0+ (Windows Server 2012) web server. This version supports Server Name Indication certificates. Microsoft calls this SSL Scalability in IIS 8.0. Because of SNI, or SSL-scalability, support in Windows Server 2012, the ASP.NET System.Net.WebException went away.
Do you want to learn how to fix a System.Collections.Generic.KeyNotFoundException "The given key was not present in the dictionary" exception? Or monitor ASP.NET CLR Garbage Collected heap in Zabbix to prevent OutOfMemoryException's.
ASP.NET C# System.Net.WebClient test script
You can use the following C# script utilizing System.Net.WebClient to test your SSL connection:
<%@ Page Language="C#" Debug="True" %>
<%@ Import Namespace="System.Net"%>
<%
WebClient client = new WebClient();
// change www.example.com with your SSL web site
byte[] data = client.DownloadData("https://www.example.com");
Response.BinaryWrite(data);
%>
I hope this helps some of you who are experiencing the same Exception.
Psst, wondering how to enable SSL in WordPress? Or how to enforce stronger cryptography in .NET web applications? (Dutch post).