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) ?
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