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.
ClientCertificates. Add(certificate); req. Method = "POST"; req. ContentType = "application/x-www-form-urlencoded"; string postData = "login-form-type=cert"; byte[] postBytes = Encoding.
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.
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.
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).
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With