Somnath Kadam
Somnath Kadam

Reputation: 6357

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.

Upvotes: 10

Views: 28954

Answers (2)

wobuntu
wobuntu

Reputation: 422

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

Upvotes: 6

user1672994
user1672994

Reputation: 10849

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.

Upvotes: 6

Related Questions