1

I am trying to add SSL to a very basic client/server application : the client sends 2 numbers, the server computes the addition and return it to the client.

I am using a valid self signed certificates, already used in Java Application, though I did not install them in the system, I dont know if it is mandatory in C#.

Here is the server code:

using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace SimpleHttpServerSSL
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string serverCertPath = "C:\\server\\server_key.p12";  
            string serverCertPassword = "myPassword"; 
            

            const string url = "https://localhost:8080/add"; 
            var server = new HttpListener();
            server.Prefixes.Add(url + "/");
            server.Start();
            Console.WriteLine("Server is listening at " + url);

          

            while (true)
            {
                HttpListenerContext context = await server.GetContextAsync();
                HandleClientRequest(context, serverCertPath, serverCertPassword);
            }
        }

        private static void Log(string message)
        {
            Console.WriteLine("Log: " + message);
        }

        private static void HandleClientRequest(HttpListenerContext context, string serverCertPath, string serverCertPassword)
        {
            try
            {
                if (context.Request.HttpMethod == "POST")
                {
                    var data = new
                    {
                        A = 0,
                        B = 0
                    };

                    using (var reader = new StreamReader(context.Request.InputStream))
                    {
                        string requestBody = reader.ReadToEnd();
                        data = JsonConvert.DeserializeAnonymousType(requestBody, data);
                    }

                    var result = data.A + data.B;

                    var responseObj = new
                    {
                        R = result
                    };

                    string responseJson = JsonConvert.SerializeObject(responseObj);
                    byte[] buffer = Encoding.UTF8.GetBytes(responseJson);
                    context.Response.ContentLength64 = buffer.Length;
                    context.Response.OutputStream.Write(buffer, 0, buffer.Length);
                    context.Response.OutputStream.Close();

                    Console.WriteLine($"Received request: A = {data.A}, B = {data.B}, Result = {result}");
                }
                else
                {
                    context.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                }

                context.Response.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception happend: " + ex.ToString());
            }
        }
            
    }
}

And here is the client code:

using System;
using System.Globalization;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace SimpleHttpClient
{
    class Program
    {

        static void LogException(Exception error)
        {
            Console.WriteLine("Exception: " + error.GetBaseException().ToString());
        }
        static async Task Main(string[] args)
        {
            try
            {
                Console.WriteLine("Enter value for A:");
                int a = int.Parse(Console.ReadLine());

                Console.WriteLine("Enter value for B:");
                int b = int.Parse(Console.ReadLine());

                var data = new
                {
                    A = a,
                    B = b
                };

                string jsonData = JsonConvert.SerializeObject(data);
                var content = new StringContent(jsonData, Encoding.UTF8, "application/json");

                string clientCertPath = "C:\\client\\client.pfx";  
                string clientCertPassword = "myPassword";  


                var handler = new HttpClientHandler(); 
                handler.ClientCertificates.Add(new X509Certificate2(clientCertPath, clientCertPassword));

                handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };


                using (var httpClient = new HttpClient(handler))
                {
                    const string serverUrl = "https://localhost:8080/add"; 

                    var response = await httpClient.PostAsync(serverUrl, content);

                    if (response.IsSuccessStatusCode)
                    {
                        string responseBody = await response.Content.ReadAsStringAsync();
                        var resultObj = JsonConvert.DeserializeAnonymousType(responseBody, new { R = 0 });
                        Console.WriteLine("Result: " + resultObj.R);
                    }
                    else
                    {
                        Console.WriteLine("Error: " + response.StatusCode);
                    }
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
            }
        }
    }
}

I have a SocketException, connection closed by distant host:

Exception: System.Net.Sockets.SocketException (10054): Une connexion existante a dû être fermée par l'hôte distant.
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.CreateException(SocketError error, Boolean forAsyncThrow)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ReceiveAsync(Socket socket, CancellationToken cancellationToken)
   at System.Net.Sockets.Socket.ReceiveAsync(Memory`1 buffer, SocketFlags socketFlags, Boolean fromNetworkStream, CancellationToken cancellationToken)
   at System.Net.Sockets.NetworkStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
   at System.Net.Security.SslStream.FillHandshakeBufferAsync[TIOAdapter](TIOAdapter adapter, Int32 minSize)

How can I fix the code / debug if anything is wrong during handshake without using Wireshark (too many restictions by our IT, it may not work properly) ?

Anas
  • 131
  • 10

1 Answers1

1

I think your HttpListener is not using your self signed certificate. You need to associate the certificate to the port 8080 using netsh command with these arguments

netsh.exe http add sslcert ipport=0.0.0.0:{0} certhash={1} appid={{2}}

Where {0} is your port.

Where {1} is your certificate thumbprint installed in your certificate store.

Where {2} is a GUID that represents your application.

A complete description for netsh command. Or you could use kestrel that does not need any system command to use a certificate

Another thread dealing with HTTPS and HttpListener

Mad hatter
  • 569
  • 2
  • 11
  • This means that I have to install the certifiates in the system. I dont want to install test certificates. I think that it must work without that – Anas Aug 01 '23 at 07:28
  • HttpListener uses HTTP.sys and you won't be able to use it without that, if I'm not mistaken. You can switch to kestrel though ;) – Mad hatter Aug 01 '23 at 07:31
  • 1
    You are right, it worked, but I had to import the Root.p12 and Server.p12 to local machine (import to current user did not work (netsh returned error 1312) ). – Anas Aug 01 '23 at 10:43