Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Client certificate with HttpClient in c#

Want to send data with client certificate (.p12 or .pfx) from Windows application to Server machine, Windows application developed in .Net Framework 4.6, OS is windows 10.

When hit from postman with client certificate (.p12 or .pfx) [Loaded in setting tab -> Add client certificate - > put hostname, select pfx file, put password], all working properly (client certificate send to server machine), but issue from below c# code,

X509Certificate2 certificate = new X509Certificate2(certificateFilePath, "password");
WebRequestHandler handler = new WebRequestHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateValidationCallback = (a, b, c, d) => { return true; };
handler.ClientCertificates.Add(certificate);


HttpClient request = new HttpClient(handler);
request.DefaultRequestHeaders.Add("User-Agent", UserAgent);
// added other headers and data
var result = request.PostAsync(url, byteContent).Result;
string resultContent = result.Content.ReadAsStringAsync().Result;

Also cross check with fiddler for Postman hit and c# hit.

When server not receive client certificate, it return 403 error.

like image 851
Somnath Kadam Avatar asked Oct 06 '19 04:10

Somnath Kadam


People also ask

How to send client certificate in HTTP request c#?

ClientCertificates. Add(certificate); req. Method = "POST"; req. ContentType = "application/x-www-form-urlencoded"; string postData = "login-form-type=cert"; byte[] postBytes = Encoding.

What is the use of client certificate?

Client Certificates are digital certificates for users and individuals to prove their identity to a server. Client certificates tend to be used within private organizations to authenticate requests to remote servers.

What is the use of HttpClient in C#?

HttpClient class provides a base class for sending/receiving the HTTP requests/responses from a URL. It is a supported async feature of . NET framework. HttpClient is able to process multiple concurrent requests.


2 Answers

I assume that your handler has no access to the private key for authentication.

This may be caused due to line 1 in your example, in which you import the certificate with the default key storage flags. Of course this is just a guess and I don't have your certificate to check this, but you can verify that by calling

// Sample for RSA, use DSA if required
var privateKeyParams = ((RSA)certificate.PrivateKey).ExportParameters(true);

which will cause a CryptographicException ("Not supported" or similar) if the key parameters cannot be accessed.

Please try the following instead for loading the certificate:

X509Certificate2 certificate = new X509Certificate2(
  certificateFilePath, "password",
  X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet);

Just a further sidenote; If you use this in production code, be sure to extend your server certificate validation callback (your 4th line) to actually validate the server certificate. See X509Chain.Build, which also allows you to modify the validation options to your needs (what the path validation actually does can be read in RFC5280).

like image 137
wobuntu Avatar answered Dec 06 '22 00:12

wobuntu


The HttpStatus code 403 can caused by TLS issues due to not invoking API with the expected server TLS version. You can check the outcome of resultContent from the code line string resultContent = result.Content.ReadAsStringAsync().Result;

To set the SslProtocol, either you can set at Handler like (if you are targeting .Net 4.7 onwards or .Net core)

WebRequestHandler handler = new WebRequestHandler();
handler.SslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;

or at application level using ServicePointManager in Startup method (or .Net framework version before 4.7)

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

A side note suggestion - I would suggest you to use pure async/await pattern. Don't use the sync call on the IO request by invoking .Result.

like image 26
user1672994 Avatar answered Dec 05 '22 23:12

user1672994