Search code examples
c#httphttpstcpclienttcplistener

c# SSL TCPServer stuck at SsLStream.AuthenticateAsServer()


Storyline:

I wanted to make my very own webserver in c#(first attempt). It went well(I was using Visual Studio to code the application and Firefox to check if I was doing right) and I managed to make a basic TCPServer. As I was trying to add SSL support to it I encountered a problem.

  • I have been trying to authenticate as a TcpServer with SSL support using SSLStream.AuthenticateAsServer([self-signed certificate]) for the last 7 days

Problem:

As I get the [Upgrade-Insecure-Requests: 1] from my beloved Firefox I'm trying to [SSLStream.AuthenticateAsServer([self-signed certificate])]. Doing that my code is stuck(but doesn't freeze/crash) at this line while my Firefox is just loading forever without seeming to send me a Response.

Code:


starting my server


TCPServer.ServerStart(8080);


(listener is being defined in my TCPServer class)

internal static TcpListener listener;

async internal static void ServerStart(int port)
    {
        if (listener == null)
        {
            listener = new TcpListener(IPAddress.Any, port);
        }
        listener.Start();

        //clients
        await Task.Run(()=> SucheNachBesuchern());
        listener.Stop();
    }


accepting clients


private static void SucheNachBesuchern(){
        TcpClient Besucher;

        while (true)
        {
            Besucher = listener.AcceptTcpClient();
            ThreadPool.QueueUserWorkItem(ThreadProzess, Besucher);
        }
    }


handling accepted clients


private static void ThreadProzess(object Besucher) {
        TcpClient besucher = (TcpClient)Besucher;
        Abfertige(besucher);
        besucher.Close();
        besucher.Dispose();
    }

private static void Abfertige(TcpClient Besucher)
    {
        //Reading the Request
        StreamReader Auftrag = new StreamReader(Besucher.GetStream());
        List<String> AuftragNachricht= new List<String>();
        while (Auftrag.Peek()!=-1) {
            AuftragNachricht.Add(Auftrag.ReadLine());
        }

        //Anfrage = request Class with bool Anfrage.SSLAnfrage being true
        //if the request contains 'Upgrade-Insecure-Requests: 1'
        Anfrage Anfrage = Anfrage.VerarbeiteAuftrag(AuftragNachricht);

        if (Anfrage.SSLAnfrage)// = if([request conatined 'Upgrade-Insecure-Requests: 1'])
        {
            //opening an SslStream to the TcpClient Besucher
            SslStream SSLStream = new SslStream(Besucher.GetStream(), false);
            try
            {
                //Authenticating as TcpServer supporting SSL !CODE IS STUCK AT THIS LINE!
                SSLStream.AuthenticateAsServer([SELFSINGEDX509CERTIFICATE.cer using openssl pkcs12], clientCertificateRequired: false, enabledSslProtocols: System.Security.Authentication.SslProtocols.Default, checkCertificateRevocation: false);

                //set timeouts for read and write
                SSLStream.ReadTimeout = 5000;
                SSLStream.WriteTimeout = 5000;

                //tryig to catch my Firefox as new client on SSL port 443
                TcpListener SSLListener = new TcpListener(IPAddress.Parse("192.168.178.72"), 443); 
                SSLListener.Start();
                TcpClient SSLBesucher = SSLListener.AcceptTcpClient();
                Debug.WriteLine("I'VE GOT A CLIENT HERE!!!!111");
            }
            catch (Exception Error) {
                Debug.WriteLine($"---Error gefangen: {Error.ToString()}");
            }
        }//[...more Code]


(For not having any knowledge in SSL I used this example-code)


Solution

  • I made it! The problem was that (in my thread) I was using a X509Certificate public-made cert with no keys in it instead of a X509Certificate2 private-cert with keys in it. That's why I got hangs in my code at SslStream.AuthenticateAsServer();. @Vasiliy Faronov answer did also help me very much(I needed to add a 307 header to port 443), thx.

    Anyways here's an example of how to send an index.html to your webbrowser through https in tcp-level:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Net;
    using System.Net.Security;
    using System.Net.Sockets;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    namespace SSLTEST
    {
        class Program
        {
            static X509Certificate2 CER = new X509Certificate2("privatecert.cer","pass");// Has to be a private X509cert with key
            static void Main(string[] args)
            {
                TcpListener listener = new TcpListener(IPAddress.Parse("192.168.178.72"), 443);
                listener.Start();
                TcpClient client = listener.AcceptTcpClient();
                Console.WriteLine("client accepted");
    
                SslStream stream = new SslStream(client.GetStream());
                stream.AuthenticateAsServer(CER, false, System.Security.Authentication.SslProtocols.Tls12, false);
                Console.WriteLine("server authenticated");
    
    
                Console.WriteLine("----client request----");
                Decoder decoder = Encoding.UTF8.GetDecoder();
                StringBuilder request = new StringBuilder();
                byte[] buffer = new byte[2048];
                int bytes = stream.Read(buffer, 0, buffer.Length);
    
                char[] chars = new char[decoder.GetCharCount(buffer, 0, buffer.Length)];
                decoder.GetChars(buffer, 0, bytes, chars, 0);
    
                request.Append(chars);
    
                Console.WriteLine(request.ToString());
                Console.WriteLine("---------------------");
    
                String method = request.ToString().Split('\n')[0].Split(' ')[0];
                String requestedfile = request.ToString().Split('\n')[0].Split(' ')[1];
                if (method == "GET" & requestedfile == "/")
                {
                    FileStream datastream = new FileInfo(Environment.CurrentDirectory+@"\"+"index.html").OpenRead();
                    BinaryReader datareader = new BinaryReader(datastream);
                    byte[] data = new byte[datastream.Length];
                    datastream.Read(data, 0, data.Length);
                    datastream.Close();
    
                    StringBuilder header = new StringBuilder();
                    header.AppendLine("HTTP/1.1 200 OK");
                    header.AppendLine("Content-Length: "+data.Length);
                    header.AppendLine();
    
                    List<Byte> responsedata = new List<byte>();
                    responsedata.AddRange(Encoding.ASCII.GetBytes(header.ToString()));
                    responsedata.AddRange(data);
    
                    stream.Write(responsedata.ToArray(), 0, responsedata.ToArray().Length);
                    Console.WriteLine("- response sent");
                }
    
                stream.Close();
                Console.WriteLine("done!");
                Console.Read();
            }
        }
    }