System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.

One of my applications was supposed to use image having its URL. The Image was instantiated with the stream the server that stored the image file responded with after being requested as below:

// create a request for image
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(IMAGE_URL);
request.Method = "GET";
request.ContentType = "multipart/form-data";
request.UserAgent = "Mozilla/4.0+(compatible;+MSIE+5.01;+Windows+NT+5.0";
request.Proxy.Credentials = CredentialCache.DefaultCredentials;
request.Credentials = CredentialCache.DefaultCredentials;

// get response from the server
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream resStream = response.GetResponseStream();

// create image from stream
Image image = Image.FromStream(resStream);

It worked like a charm when TCP port was used (regular, non-decure connection) but whenever the image was available at a URL that was secured (https://xxx.xx) I was getting the following stack trace:

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.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 ---

Solution

According to Microsoft Help and Support the call was not authorised correctly and therefore request failed.

In that article it’s suggested (there are examples of code as well) to create custom implementation of ICertificatePolicy. It would be passed to ServicePointManager.CertificatePolicy before the call to HTTPS://XXX.XX is made. That implementation should define public bool CheckValidationResult method that would always return true so that any request under SSL is accepted (which of course would weaken the security).

It’s worth adding that ServicePointManager.CertificatePolicy is deprecated and it’s suggested to use ServicePointManager.ServerCertificateValidationCallback instead. So the following line should be added before you try to create a connection:

ServicePointManager.ServerCertificateValidationCallback += 
delegate(
    object sender, 
    X509Certificate certificate, 
    X509Chain chain, 
    SslPolicyErrors sslPolicyErrors)
    {
         return true;
    };
 System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.

46 Responses to “System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.”


  • Thanks for this – I’ve been struggling with this problem all day. Please note that I think the code at the end of you post should be:

    ServicePointManager.ServerCertificateValidationCallback =
    new RemoteCertificateValidationCallback(
    delegate(
    object sender2,
    X509Certificate certificate,
    X509Chain chain,
    SslPolicyErrors sslPolicyErrors)
    {
    return true;
    });

  • Lee,

    It’s great I helped you!
    Speaking of the code – I double checked it and I have in my code what I presented in the post and it works like a charm.
    Probably both versions are fine.

    Thanks,
    Jarek

  • You’re right – for some reason I couldn’t get the example shown to work before, but of course now it does :-)

    The difference in our code is that your version adds a delegate whereas mine sets it – so if there was an existing delegate also listening then yours would retain it and mine would replace it.

    There is a potential issue here because ServicePointManager doesn’t go out of scope, so the delegate remains defined on the ServerCertificateValidationCallback. If the += version is called everytime a request is made, we’d get a build up of anonymous delegates – that couldn’t easily be removed (or even detected apart from through potentially odd behaviour if we were calculating whether to accept the call or not).

    Anyway, it really did help solve me problem so thank again!

  • Lee,

    So what you are suggesting is assigning a delegate instead of adding a new one per each request, i.e. using = instead of +=, right?

    Jarek

  • If there is a situation in which multiple delegates responding to this callback makes sense, then += will retain any existing delegates. I’m not sure whether this works in practice – whether the callbacks will all be called until one returns true – or if they all have to return true – or other?

    I don’t have enough knowledge of this process to know whether that this a requirement/good idea or not but my gut feeling is that it isn’t.

    I think what should actually happen is that if we just want to return true all the time, then assigning (using =) an anonymous delegate once (no need to keep creating and reassigning it) is fine.

    If there is to be any logic to whether to return true or false, then it would make sense to create a “real” named method and set it as the handler for this callback.

  • Katerina Pilchova

    Czescz Jarek,

    thank you so much, it really helped me :)

    katka

  • This method is now deprecated. Below is the new code. Thanks for pointing me in the right direction.

    System.Net.ServicePointManager.ServerCertificateValidationCallback += delegate(object sender,
    System.Security.Cryptography.X509Certificates.X509Certificate certificate,
    System.Security.Cryptography.X509Certificates.X509Chain chain,
    System.Net.Security.SslPolicyErrors sslPolicyErrors) { return true; };

  • my issue was also solved…
    thanks..

  • This helped me out too. Thanks much!

  • Thanks for the solution. It worked.

  • Thanks. work like a charm.

  • Thanks, this fixed my problem. I was receiving this error when calling the “Campaign Monitor” API.

  • +=1.
    This solution worked like a charm for me as well. Problem solved ;-)

  • Thank you! Worked first time.

  • WOW. Thankyou so much. Helped me to solved my problem.

  • Thank you for code. I worked a lot of work.

  • I hope most of you aren’t really going with this as your solution, to weaken security in today’s market place is a HUGE mistake, who ever offered the original article at microsoft should be flogged. This might be fine for serving some images, but many people have issues with other types of secure connections, and then you sure the heck better not simply return true at all times.

  • John, I am still looking for a perfect solution. I was lucky, I ran across Jarosław solution. The solutions I found would have taken too much time to assemble/test (as my boss says in v1 80/20 rule always applies). If you find the solution, please do post so I and others can learn from it better improve the security.

  • It simply worked.

    G8 Man!!!

  • Using this code for a WebService client (WCF basicHttp via DataPower & HTTPS), the Callback do not seems to be called :(

  • The shortened Lambda version which also works here for a WCF webservice call is:

    ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;

  • Koen,

    I’d say Lambda expression looks much better here. Of course it does the same as the delegate, but once you’ve learned it you always choose it :) At the time of writing the post my knowledge here was worse.

    Jarek

  • Jaroslaw, would it be possible for you to post the code in whole with the solution embedded? Cheers in advance.

  • Carl,

    Sorry I no longer can access it…

    Jarek

  • Great Post. thanks a lot i was searching out for a long while to resolve this problem.

    much appreciated……

  • thanks for the solution.. it helped alot.. my issue got resolved!!!

  • Thanks,

    The code worked for me as well. The remote server certificate had expired in our dev environment and was preventing the secure handshake from completing. In my case I’m calling internal servers so I was less worried about the expired certificate – I agree with John that you shouldn’t blindly “pass” the validation but at least the resulting handshake still results in an encrypted channel.

    An enhancement to the solution might be to add some notification code in the callback code to let an administrator know there is a certificate problem with one of their servers.

  • Its a good nice work-around.

    Thanks!!

    I am not sure whether it is good to use a work-around for such kind of authentication issues. its a-kind of by-passing some security measures. As it is not going through a complete security check.

    Please let me know if there are some areas that we can look before using such work-around.

    Regards
    Kapils

  • Thanks for this, resolved an issue upgrading a service we have.

    you can shorten your policy call back to:

    ServicePointManager.ServerCertificateValidationCallback += delegate { return true; };

  • really its very helpful..

  • thanks! this works for me!

  • Thank u very much.Fixed my issue.

  • Implementing this solution does solve the issue, but now i am not sure of thefact, whether we are bypassing certification check of our SMTP server or server which is receiving the mail.

    Essentially i am trying to send mail using (TLS) within a webpart embded in SharePoint environment.

    Any pointers on security risks, involved??

  • Thanks, and to Lee Oades as well. I had to use his code to get it working.

  • Thanks for this great piece of code, it save my lot of time.

  • Just wish to check, my development application as client is function well when the server is connect to http web service.

    now i try to move to other environment which having setup of https (or ssl). then i hit the error below:-

    “The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel”

    I already install the certificate provided by the web service. I try to use SOAPUI as tools to check it working fine from my client to the server. but my application just not work.

    need help urgently. thanks a lot

  • Thanks for the code, You are the champ and your code worked like a charm…

  • Terence, have you followed suggestions from my post? Also, from the top of my head, have you tried making the certificate trusted? This can be done with Certificates MMC Snap-in.

    Jarek

  • Thanks. work like a charm..

  • Using Go Daddy certificates?

    When you look at the Go Daddy Certification Path of that certificate on the web server, do you see Go Daddy Class xxx or Starfield Class xxx ?

    And from your non-legacy client i.e Windows Vista upwards, what does the Go Daddy Certification Path display? Go Daddy Class xxx or Starfield Class xxx ?

    And these clients that get the warning, are they legacy clients? i.e WinXP and older?

    Root Certificate updates works differently as of Windows Vista.

    http://support.microsoft.com/kb/931125

    Root certificates on Windows Vista and later are distributed via the automatic root update mechanism ? that is, per root certificate. When a user visits a secure Web site (by using HTTPS SSL), reads a secure email (S/MIME), or downloads an ActiveX control that is signed (code signing) and encounters a new root certificate, the Windows certificate chain verification software checks Microsoft Update for the root certificate. If it finds it, it downloads the current Certificate Trust List (CTL) containing the list of all trusted root certificates in the Program, and verifies that the root certificate is listed there; it then downloads the specified root certificate to the system and installs it in the Windows Trusted Root Certification Authorities Store.

    You’ll probably find that your Go Daddy Certification Path on the web server thinks it’s Starfield Class 2 instead of Go Daddy Class 2 so you installed the wrong root certificate. It caught me out as when you view in on the web server it doesn’t display a root certificate warning, download and install the Do Daddy class 2 root cert and remove the Starfield one and your problem should dissapear.

  • Worked for me too. thanks.

  • Sir, your solution is spot-on for my current application.
    Here is a variation using lambda expressions.

    ServicePointManager.ServerCertificateValidationCallback +=(sender, certificate, chain, sslPolicyErrors) => true;

  • Hi, I did use the same code. I manage to get the response value, but system still show me same error.

  • Thanks you man you save me, works perfect !!!

Leave a Reply