I'm not using .NET for my client, but server side it can be configured simply via IIS by deploying my ASP.NET Core website behind IIS, configuring IIS for HTTPS + client certificates:
IIS client certificate setting:
Then you can get it simply in the code:
var clientCertificate = await HttpContext.Connection.GetClientCertificateAsync();
if(clientCertificate!=null)
return new ContentResult() { Content = clientCertificate.Subject };
It's working fine for me, but I'm using curl or chrome as clients, not the .NET ones. During the HTTPS handshake, the client gets a request from the server to provide a certificate and send it to the server.
If you are using a .NET Core client, it can't have platform-specific code and it would make sense if it couldn't connect itself to any OS specific certificates store, to extract it and send it to the server. If you were compiling against .NET 4.5.x then it seems easy:
It's like when you compile curl. If you want to be able to connect it to the Windows certificates store, you have to compile it against some specific Windows library.
I ran a fresh install for my platform (Linux Mint 17.3) following these steps: .NET Tutorial - Hello World in 5 minutes. I created a new console application targeting the netcoreapp1.0 framework, was able to submit a client certificate; however, I did receive "SSL connect error" (CURLE_SSL_CONNECT_ERROR 35) while testing, even though I used a valid certificate. My error could be specific to my libcurl.
I ran the exact same thing on Windows 7 and it worked exactly as needed.
// using System.Net.Http;
// using System.Security.Authentication;
// using System.Security.Cryptography.X509Certificates;
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = SslProtocols.Tls12;
handler.ClientCertificates.Add(new X509Certificate2("cert.crt"));
var client = new HttpClient(handler);
var result = client.GetAsync("https://apitest.startssl.com").GetAwaiter().GetResult();
I have a similar project where I communicate between services as well as between mobile and desktop with a service.
We use the Authenticode certificate from the EXE file to ensure that it's our binaries that are doing the requests.
On the requesting side (over simplified for the post).
Module m = Assembly.GetEntryAssembly().GetModules()[0];
using (var cert = m.GetSignerCertificate())
using (var cert2 = new X509Certificate2(cert))
{
var _clientHandler = new HttpClientHandler();
_clientHandler.ClientCertificates.Add(cert2);
_clientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
var myModel = new Dictionary<string, string>
{
{ "property1","value" },
{ "property2","value" },
};
using (var content = new FormUrlEncodedContent(myModel))
using (var _client = new HttpClient(_clientHandler))
using (HttpResponseMessage response = _client.PostAsync($"{url}/{controler}/{action}", content).Result)
{
response.EnsureSuccessStatusCode();
string jsonString = response.Content.ReadAsStringAsync().Result;
var myClass = JsonConvert.DeserializeObject<MyClass>(jsonString);
}
}
I then use the following code on the action that gets the request:
We rather provide a 404 than a 500 as we like those that are trying URLs to get a bad request rather then let them know that they are "on the right track"
In .NET Core, the way to get the certificate is no longer by going over Module. The modern way that might work for you is:
private static X509Certificate2? Signer()
{
using var cert = X509Certificate2.CreateFromSignedFile(Assembly.GetExecutingAssembly().Location);
if (cert is null)
return null;
return new X509Certificate2(cert);
}