10

I'm writing a client for a REST API and to authenticate to the API I must use a cert that was provided to me.

this code is as follows:

public string GetCustomer(int custId)
{
X509Certificate2 Cert = new X509Certificate2();
    Cert.Import(@"C:\users\foo\desktop\api\pubAndPrivateCert.pkcs12", "", X509KeyStorageFlags.PersistKeySet);

    ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertificate;
    HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://api.foo.net/api/customer/v1/" + custId);
    req.ClientCertificates.Add(Cert);

    req.UserAgent = "LOL API Client";
    req.Accept = "application/json";
    req.Method = WebRequestMethods.Http.Get;

    string result = null;
    using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
{
StreamReader reader = new StreamReader(resp.GetResponseStream());
result = reader.ReadToEnd();
}
return result;
}

Each time I make the request I get an error 400 and when using Fiddler to look at the response I get the following

<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/0.6.32</center>
</body>
</html>

I see no reason for it not to send the cert but troubleshooting SSL isn't terribly easy. I did add some debug statements to add some detail and stopped using fiddler and he is what I got

These errors are from ValidateServerCertificate()

Certificate error: RemoteCertificateChainErrors
NotSignatureValid
The signature of the certificate cannot be verified.
1048576
Unknown error.

These are the errors from the WebExecption that is thrown.

Cought Exception ProtocolError
The remote server returned an error: (400) Bad Request.
BadRequest
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/0.6.32</center>
</body>
</html>

This is the ValidateServerCertificate() code..it always returns true to ignore any cert errors.

    public static bool ValidateServerCertificate(object sender, X509Certificate certificate,X509Chain chain,SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
            return true;

        if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors)
        {
            Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
            foreach (var chainstat in chain.ChainStatus)
            {
                Console.WriteLine("{0}", chainstat.Status);
                Console.WriteLine("{0}", chainstat.StatusInformation);
            }
            return true;
        }

        Console.WriteLine("Certificate error: {0}", sslPolicyErrors);

        // allow this client to communicate with unauthenticated servers. 
        return true;
    }
user961346
  • 301
  • 2
  • 3
  • 17
  • Debugging client certificates with Fiddler is not really possible as Fiddler will have to send its own cert... Check if you see any exceptions (including handled ones) in your process... – Alexei Levenkov May 04 '14 at 00:41
  • 2
    Fiddler can [Respond to Requests Requiring a Client Certificate](http://docs.telerik.com/fiddler/configure-fiddler/tasks/respondwithclientcert). – Joel Allison May 04 '14 at 18:48

1 Answers1

9

Your code loads the client certificate from a local file. You should have more success if you import the client certificate into the certificate store (which is highly recommended to protect the private key). Then your code should look more like this:

public string GetCustomer(int custId)
{
    // EDIT THIS TO MATCH YOUR CLIENT CERTIFICATE: the subject key identifier in hexadecimal.
    string subjectKeyIdentifier = "39b66c2a49b2059a15adf96e6b2a3cda9f4b0e3b";

    X509Store store = new X509Store("My", StoreLocation.CurrentUser);
    store.Open(OpenFlags.ReadOnly);

    X509Certificate2Collection certificates = store.Certificates.Find(X509FindType.FindBySubjectKeyIdentifier, subjectKeyIdentifier, true);
    X509Certificate2 certificate = certificates[0];

    HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://api.foo.net/api/customer/v1/" + custId);
    req.ClientCertificates.Add(certificate);

    req.UserAgent = "LOL API Client";
    req.Accept = "application/json";
    req.Method = WebRequestMethods.Http.Get;

    string result = null;
    using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
    {
        StreamReader reader = new StreamReader(resp.GetResponseStream());
        result = reader.ReadToEnd();
    }
    return result;
}

See Import a Certificate for instructions. The code assumes that you have imported the certificate with both public and private keys to the Personal certificates folder ("My") of the Current User.

You do not need to supply a ServicePointManager.ServerCertificateValidationCallback. That allows your application to change how the server certificate is validated. That does not influence how the server validates your client certificate.

Joel Allison
  • 2,091
  • 1
  • 12
  • 9
  • 1
    although I would prefer to do it this way it doesn't fit my use case. This service will run in Azure and scale up and down as needed... so I would have to write code that installs a certificate to the VM before launching my application which feels very "hacky" – user961346 May 05 '14 at 14:16
  • Strictly for troubleshooting, if you install the supplied certificate, can you successfully browse to the REST API? Can Composer in Fiddler (with client certificate configured) connect? – Joel Allison May 05 '14 at 15:00
  • @user961346 did you ever solve this? Having the same issues as you. – eth0 Oct 11 '14 at 20:12
  • 8
    @user961346 - "change the format of my certificate" ... to what? I'm having the same issue now - would love to hear how you fixed this. – Jochen van Wylick Nov 18 '15 at 09:28
  • Where do I find or how do i get 'the subject key identifier in hexadecimal'? – FiveTools Feb 27 '16 at 17:38
  • How did you fix this? – danyim Apr 28 '16 at 21:26
  • @user961346 Can you elaborate on that `I had to change the format of my certificate.`? You mean the file format? What did you use? I use P12 that contains a private key and it still doesn't work. – user2173353 Jul 26 '16 at 13:27
  • Having the same issue. Tried both local file and store certificates. Works flawlessly in Fiddler but not in my Console Application. Did anyone ever manage to fix this issue? – Vidhyardhi Gorrepati Feb 21 '17 at 23:04
  • Been struggling with this issue for few days now, anyone know how to solve this issue? – Shibu Thannikkunnath Oct 18 '19 at 20:55