12

I currently have this code that makes an SSL connection to a server:

using (client = new TcpClient())
{

    client.Connect(Hostname, Port);
    var callback = new RemoteCertificateValidationCallback(ValidateServerCertificate);


    using (stream = new SslStream(client.GetStream(), false, callback))
    {
        stream.AuthenticateAsClient(Hostname);
    }
}

However, I don't think it supports SNI because the wrong certificate is being returned from the SNI configured server. Is there anyway to make the SSL connection using SNI?

I am using .NET 2 (willing to upgrade if necessary). I am using Windows 7, but would like the software to work on other platforms such as Windows 2008 if possible.

TonyM
  • 708
  • 1
  • 8
  • 15
  • It would help to know in details which versions of .Net and Windows you're using, since SNI support will depend on those. – Bruno Mar 30 '12 at 16:24
  • @Bruno Good point - I've updated the question. – TonyM Mar 30 '12 at 16:32
  • 2
    It looks like you might need an external library instead of `SslStream`: http://connect.microsoft.com/VisualStudio/feedback/details/729925/net-4-4-5-sslstream-no-supports-the-tls-server-name-indication-sni – Bruno Mar 30 '12 at 17:48
  • 1
    http://connect.microsoft.com/VisualStudio/feedback/details/729925/net-4-4-5-sslstream-no-supports-the-tls-server-name-indication-sni Microsoft knows, and doesn't care – Bruno Rohée Nov 21 '13 at 14:38
  • Hi @TonyM did you ever get a working solution to this? I also need a direct sockets connection using TLS with SNI (not HTTPS) and cannot find a solution. – joelc Apr 04 '16 at 17:31

4 Answers4

2

So to conclude it seems as if the answer is: No, there is no way that you can make the TLS connection use SNI :-(

I couldn't find any C# example code making a TLS connection with SNI. Theoretically though, it should be possible to create that manually, i.e. as per How to implement Server Name Indication (SNI)

however, an attempt over here failed: TLS SNI with C#

Community
  • 1
  • 1
Frederick Nord
  • 1,246
  • 1
  • 14
  • 31
0

The answer seems to be "yes", as there is a C# implementation that does support SNI. This bug report mentions the version of mono that supports HTTPS requests with SNI.

Frederick Nord
  • 1,246
  • 1
  • 14
  • 31
0

We were faced similar issue trying to connect to business API using mutual SSL. Standard .NET libraries do not work. You can use third-party library http://curl.haxx.se/libcurl/ as one of the possible solutions.

libcurl is a free and easy-to-use client-side URL transfer library, supporting DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet and TFTP. libcurl supports SSL certificates, HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload, proxies, cookies, user+password authentication (Basic, Digest, NTLM, Negotiate, Kerberos), file transfer resume, http proxy tunneling and more.

.NET library can be uploaded from http://sourceforge.net/projects/libcurl-net/

When you upload it change libcurl.dll to the latest version you can find (it should be 7.18.1 or newer). In current libcurl-nel package it is older than is needed.

 public string HTTPGet(string URL, string Proxy, string certName = null, string certPassword = null)
 {
    Easy easy = new Easy();
    SockBuff = "";
    try
    {
        Easy.WriteFunction wf = new Easy.WriteFunction(OnWriteData);
        easy.SetOpt(CURLoption.CURLOPT_URL, URL);
        easy.SetOpt(CURLoption.CURLOPT_TIMEOUT, "60");
        easy.SetOpt(CURLoption.CURLOPT_WRITEFUNCTION, wf);
        easy.SetOpt(CURLoption.CURLOPT_USERAGENT, UserAgent);
        easy.SetOpt(CURLoption.CURLOPT_COOKIEFILE, CookieFile);
        easy.SetOpt(CURLoption.CURLOPT_COOKIEJAR, CookieFile);
        easy.SetOpt(CURLoption.CURLOPT_FOLLOWLOCATION, true);

        if (!string.IsNullOrEmpty(certName))
        {
            easy.SetOpt(CURLoption.CURLOPT_SSLCERT, certName);
            if (!string.IsNullOrEmpty(certPassword))
            {
                easy.SetOpt(CURLoption.CURLOPT_SSLCERTPASSWD, certPassword);
            }
        }

        if (URL.Contains("https"))
        {
            easy.SetOpt(CURLoption.CURLOPT_SSL_VERIFYHOST, 1);
            easy.SetOpt(CURLoption.CURLOPT_SSL_VERIFYPEER, 0);
        }

        if (!string.IsNullOrEmpty(Proxy))
        {
            easy.SetOpt(CURLoption.CURLOPT_PROXY, Proxy);
            easy.SetOpt(CURLoption.CURLOPT_PROXYTYPE, CURLproxyType.CURLPROXY_HTTP);
        }

        var code = easy.Perform();
        easy.Cleanup();
        Console.WriteLine(code);
    }
    catch
    {
        Console.WriteLine("Get Request Error");
    }

    return SockBuff;
}

public static Int32 OnWriteData(Byte[] buf, Int32 size, Int32 nmemb, Object extraData)
{
    // Console.Write(System.Text.Encoding.UTF8.GetString(buf));
    SockBuff = SockBuff + System.Text.Encoding.UTF8.GetString(buf);

    return size * nmemb;
}
Sergey
  • 21
  • 1
  • 4
-2

I know this is an older post, but I recently needed the same thing. I have an application in C# that I used to dynamically create websites in C#. I needed the site's bindings to set the "server name indication" flag when created, because we are trying to use multiple SSL sites on the same IP address.

As it turns out, you CAN now do this in C#. There is a flag option you can pass that will turn on the SNI option. I hope this helps anyone that has this problem. Here is an example:

// Create site using server manager
ServerManager sm = new ServerManager();
Site mySite =  sm.Sites.Add("example.com", "C:\Test Website\", 443);

// Creating binding object to store SSL cert
var ibind = mySite.Bindings.CreateElement();
ibind.Protocol = "https";
ibind.BindingInformation = "*:443:" + domain;
ibind.CertificateHash = certificate.GetCertHash();

// This option will turn on SNI (server name indication)
ibind.SetAttributeValue("sslFlags", 1);

// Add the binding to the site
mySite.Bindings.Add(ibind);
Edward B.
  • 437
  • 3
  • 10