I am developing an ASP.NET web application that sends a request to another server using HttpWebRequest. It sends the request over HTTPS, and the remote server requires a client certificate. The request fails in the .NET application, apparently unable to send the correct client certificate. I am able to successfully connect and send the client certificate if I simply visit the url with a web browser (Chrome specifically).
The code below is a simple reproduction, with just a basic GET request.
var r = WebRequest.Create(url) as HttpWebRequest;
r.ClientCertificates = new X509CertificateCollection { myX509Cert };
using (var resp = r.GetResponse() as HttpWebResponse) {
...
}
I get our favorite exception, "Could not create SSL/TLS secure channel". Typically, these types of problems point to issues with permissions on your certificate's private key. I tried everything I could think of to ensure this is all configured correctly, but perhaps I missed something. Long story short, the remote server is sending a TLS CertificateRequest
with a list that that does seem to properly identify my client certificate, but my appliation fails to respond with any client certificate.
Here is my setup:
- Windows 7 professional 64 bit
- Able to reproduce the problem in an ASP.NET MVC 3 / .NET 4 application running in the Visual Studio dev server, ASP.NET WebForms / .NET 3.5 application running in local IIS, and in a .NET console application / .NET 4
- Microsoft .NET Framework 4.5 was recently installed. Haven't examined yet whether this could be a problem
Here is everything I've tried, and what I know:
- This code seemed to work fine when running on a Windows XP machine
- I ensured that my client certificate is imported into the Local Computer, Personal Certificates store, with the Private Key permissions properly configured for myself and all relevant IIS users
- I attempted reinstalling the certificate on my machine a couple of times
- I have confirmed that the .NET application can access the X509 certificate and
HasPrivateKey
= true - Ensured that my client certificate is valid. It's actually an SSL cert for the web server on which this application will be running
- I set
PreAuthenticate = true
in the request object. Did not make a difference - I tried setting
ServicePointManager.Expect100Continue = false
, did not make a difference - I tried setting
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3
, but apparently the remote server requires TLS so that's not helping - I set a
ServicePointManager.ServerCertificateValidationCallback
delegate to always return true. But the request fails earlier in the TLS handshake, before it even gets to the point of calling this delegate - When I go to the URL in Chrome, it asks me to supply a client certificate, presents the correct client cert as a choice, I choose that cert and I get a valid response. Also works properly when I construct a request in Fiddler. So it definitely seems to be specifically a problem with my .NET code, not the certificate itself, or the remote server, etc.
- The vendor I am working with has the same service set up on a different remote server, and that service requires a different client cert. So I tried the same thing with the different URL and client cert and got the same failure
I added System.Net trace and saw this:
SecureChannel#26717201 - We have user-provided certificates. The server has specified 6 issuer(s). Looking for certificates that match any of the issuers.
SecureChannel#26717201 - Left with 0 client certificates to choose from.
...
InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=CertUnknown).
I enabled full SCHANNEL logging, and saw this warning:
The remote server has requested SSL client authentication, but no suitable client certificate could be found. An anonymous connection will be attempted. This SSL connection request may succeed or fail, depending on the server's policy settings.
I ran Wireshark, and saw the remote server sent a CertificateRequest
, and it seems to have a Distinguished Name
entry with the CN\OU\O values of my client certificate specified exactly. After that, my application sends a Certificate response with no certificates.
It seems like I am missing something with setting up this certificate to work properly with .NET applications in Windows 7. My best guess is that there is something different on my new Windows 7 machine, compared to the XP machine, that is causing this to fail now. I don't have access to a Windows XP environment at the moment to confirm this; I will in a couple of days but would really like to resolve this ASAP.
Any ideas would be much appreciated. Thanks!
EDIT As described above, when connecting to the URL in Chrome, the browser asks me for a client certificate, and I can provide the correct certificate and connect successfully. I cannot do this successfully in Internet Explorer (9), however. I simply get "Internet Explorer cannot display the webpage", with no other prompts or explanation. I was told that this may be relevant as WebRequest.Create
has similar behavior to IE. I am looking into what this might mean, but would value any thoughts on this.
EDIT Also, I should note that the remote server uses a self-signed SSL certificate. I originally thought that might be the problem, so I added the cert as a trusted root in MMC so that the certificate appears as valid on my machine. This did not fix the problem.