2

Recently, one of our ASP.NET ASHX handlers that makes a call to another ASHX handler on another server started to fail with the following error: "Unhandled Exception: System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel." Installing the certificate for the other server did not help (in any case, the other server is public-facing and has a cert signed by a CA).

I wrote a small C# program to test if I could connect to the other server from the command line:

using System.Net;

public class Test
{
    const string theUrl = "https://www.example.com/Handler.ashx?ID=123";
    public static void Main(string[] args)
    {
        var req = WebRequest.Create(theUrl);
        var resp = req.GetResponse();
    }
}

This, too, fails with a System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel. However, if I make the call to the handler from a VBScript file:

Const theUrl = "https://www.example.com/Handler.ashx?ID=123"

Dim oXmlHttp

Set oXmlHttp = WScript.CreateObject("Microsoft.XMLHTTP")
With oXmlHttp
    .Open "GET", theUrl, False
    .Send

    WScript.Echo CStr(.Status)
End With

Set oXmlHttp = Nothing

it works; the script returns a status of 200!

I checked the .NET machine.config file as I encountered a similar issue in the past where requests sent from unmanaged code worked but not from .NET because the proxy settings were different, but this is not the case here. Why should a request from VBScript work but fail from .NET?

EDIT: Enabling tracing reveals a similar error to the one reported in this question:

System.Net Information: 0 : [28468] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 1ad5f0:29f3620, targetName = www.example.com, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
System.Net Information: 0 : [28468] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=AlgorithmMismatch).
System.Net.Sockets Verbose: 0 : [28468] Socket#3741682::Dispose()
System.Net Error: 0 : [28468] Exception in the HttpWebRequest#2383799:: - The request was aborted: Could not create SSL/TLS secure channel.
System.Net Error: 0 : [28468] Exception in the HttpWebRequest#2383799::GetResponse - The request was aborted: Could not create SSL/TLS secure channel.
Community
  • 1
  • 1
Ken Keenan
  • 9,818
  • 5
  • 32
  • 49

2 Answers2

2

"Algorithm mismatch" is a bit vague, especially since TLS errors are well defined. No matter.

Run ssl-enum-ciphers (perl) or nmap's equivalent against the server to see what SSL/TLS versions and ciphers it supports, and prefers. In pinch you could try MS wfetch to do something similar, but it has quite limited protocol and cipher support. You can do the equivalent for the client using openssl as a server (key+cert required):

openssl s_server -quiet -accept 4443 -key server.key -cert server.crt

optionally mix in -no_xxx or -xxx where xxx is one of ssl2, ssl3, tls1 for variety.

(Browser friendly, but less easy to code/automate is https://www.ssllabs.com/ssltest/viewMyClient.html or see here for more.)

The way the protocol/cipher agreement works is that the client sends (ClientHello) an SSL/TLS version, a list of ciphers (and extensions), then server chooses the protocol version, and a cipher in common (intersecting set) and replies (ServerHello). The (rough) method for choosing is:

  • use the highest SSL/TLS version supported by the client, or give up (possible handshake failure)
  • prefer the first common cipher in the client list, i.e. assume it's ordered by preference
  • -or- prefer the first common cipher in the server list [1]
  • -or- give up with (no common cipher)

[1] this allows the operator to configure the server prefer ciphers based on other than "strength" (symmetric key-size), e.g. to force 128-bit RC4 over AES-256 while supporting both, as was the style a year or two back.

The default cipher order is usually by descending "strength" (symmetric cipher key size). An SSL/TLS protocol version sets out an expected set of ciphers, but supporting the same cipher (e.g. AES-128) isn't enough.

It can be the case that the client won't talk TLS, and the server won't talk SSL, e.g. https://serverfault.com/questions/208542/what-might-cause-https-failure-when-not-specifying-ssl-protocol This should cause a protocol or handshake error though.

Since forcing the protocol via SecurityProtocolType.Ssl3 (another case here), it looks like the client is proposing something the server doesn't like -- if you have confirmed both sides support TLS then it might be SNI tripping you up, and falling back to SSL3 side-steps that.

In the unlikely event that the client side is defaulting to SSlv2, the server is quite right to reject it ;-)

Community
  • 1
  • 1
mr.spuratic
  • 9,767
  • 3
  • 34
  • 24
0

We found a workaround but it's not particularly elegant. The problem started to occur when the server to which we are connecting was replaced with a load-balanced configuration. If I edit the hosts file on the client machine to talk to the server directly rather than go through the load-balancer, it works.

In addition, I verified that the code changes in this answer also work. It would still be nice to know the differences are between the managed and unmanaged implementations of SSL/TLS...

Community
  • 1
  • 1
Ken Keenan
  • 9,818
  • 5
  • 32
  • 49