3

While working on this question, I identified that the problem is slightly different than initially stated, so I am changing title and description

I'm trying to authenticate myself against WebService using my client certificate. I am using WebRequest for that purpose. I need to use self-signed certificate, without registering it in Windows X509 trusted root store.

The way I know if client certificate is presented or not is by examining request object on the server

I tried to use code from this question as a guidance. It doesn't work on .NET. Here is my code:

            var certificate = new X509Certificate2(Properties.Resources.MyCert);
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(host);
            req.ClientCertificates.Add(certificate);
            WebResponse resp = req.GetResponse();
            var stream = resp.GetResponseStream();

What I observe is that even though req.ClientCertificates does contain certificate with a valid private key, that certificate is never presented to server. I get no indication from WebClient that certificate is not used during handshake.

If I put certificate into "trusted root", the code will work (even when certificate is not in "personal").

My questions are:

  1. Since certificate is usable when it's placed in "trusted root", I assume it is likely due to policy or something of that kind. Is it possible to coerce .NET to ignore policy settings and use supplied client certificate during TLS negotiation?

  2. If abovementioned coercion is not possible, is there a call which will tell me ahead of time, that certificate I am about to use is not usable, and will be ignored? Alternatively, if such call is not available, could I make WebClient fail indicating a certificate error, instead of silently skipping over?

NOTE: I am aware that configuring certificates as described by Microsoft will work. This is NOT what I am looking for. I don't want to register potentially insecure certificate in trusted root, because this is potentially security hazard. I want to use cert on client without registering in store, or at least to get an exception indicating that certificate cannot be used. I realize that there can be multiple reasons why certificate cannot be used for a session, but there must be an exception, or at least some sort of indication on the client side that it cannot use specified cert. Instead, client simply doesn't present one.

Community
  • 1
  • 1
galets
  • 17,802
  • 19
  • 72
  • 101
  • Does it work on .NET with the very same certificate if you read it from the cert store? – Wiktor Zychla Jan 31 '14 at 19:35
  • @WiktorZychla it works if certificate exists in "personal" and "trusted root". If it is there, it will work, whether i read it from store or file or resource. – galets Jan 31 '14 at 20:46
  • Are you sure the server is requesting a client cert? – Peter Ritchie Jan 31 '14 at 21:21
  • @galets: did you try to use the overloaded constructor of the `X509Certificate2` that takes `X598KeyStorageFlags`? In particular, the `UserKeySet` imports the certificate **temporarily** to the user store. This sounds like your chance, I use this often (not in your scenario though). – Wiktor Zychla Jan 31 '14 at 21:43
  • @PeterRitchie yes, I have HttpListener, which will show certificate in HttpListenerRequest.GetClientCertificate(). If certificate is registered in x509 store of client machine, certificate is there, otherwise it is null – galets Jan 31 '14 at 21:44
  • @WiktorZychla Tried `new X509Certificate2(Properties.Resources.MyCert, "", X509KeyStorageFlags.UserKeySet);`, same behavior. I noticed something however: it is cert being in trusted root that matters, not "Personal" – galets Jan 31 '14 at 23:54
  • @WiktorZychla we might be mising a point here. Really, what I'm looking here is a way to be able to either force WebClient to ignore policy and use whatever certificate I'm giving to it, or make it throw if it cannot use certificate. Instead, what it does now is silently refusing to use certificate and I have no way to know unless I look at the server side – galets Feb 01 '14 at 00:00

1 Answers1

1

When you instantiate your X509Certificate2, is the PrivateKey property set? If it is null, you are missing the private key, meaning the SSL/TLS client will be unable to authenticate you.

Make sure you are loading the certificate from a PFX file (or similar) instead of a CER. These contain the private key, too. They are usually password protected for that purpose. See How to retrieve certificates from a pfx file with c#? for more info.

Community
  • 1
  • 1
akton
  • 14,148
  • 3
  • 43
  • 47
  • 1
    Instantiating from pfx. Private key is present. Same thing: we are missing a point here. What I'm looking here is a way to be able to either force WebClient to ignore policy and use whatever certificate I'm giving to it, or make it throw if it cannot use certificate. Instead, what it does now is silently refusing to use certificate and I have no way to know unless I look at the server side. If there is no private key I want client to throw, instead of presenting no certificate at all. Any reason really - either use certificate or throw, don't act like I didn't supply one. – galets Feb 01 '14 at 01:50