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;
    };
Reblog this post [with Zemanta]

Enjoy this Post?

Your vote will help me grow this site and provide even more information

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


  1. 1 Lee Oades

    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;
    });

  2. 2 Jarosław Dobrzański

    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

  3. 3 Lee Oades

    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!

  4. 4 Jarosław Dobrzański

    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

  5. 5 Lee Oades

    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.

  6. 6 Katerina Pilchova

    Czescz Jarek,

    thank you so much, it really helped me :)

    katka

  7. 7 Shrini Viswanathan

    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; };

  8. 8 Subhash Maji

    my issue was also solved…
    thanks..

  9. 9 Dennis Parks

    This helped me out too. Thanks much!

  10. 10 hidha

    Thanks for the solution. It worked.

  11. 11 Terence

    Thanks. work like a charm.

  12. 12 Steve Mck

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

  13. 13 T.

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

  14. 14 Alan Churchill

    Thank you! Worked first time.

Leave a Reply




Switch to our mobile site