Accessing a web service and a HTTP interface using certificate authentication

Alberto De Caro picture Alberto De Caro · Jun 22, 2012 · Viewed 18.7k times · Source

It is the first time I have to use certificate authentication. A commercial partner expose two services, a XML Web Service and a HTTP service. I have to access both of them with .NET clients.

What I have tried

0. Setting up the environment

I have installed the SSLCACertificates (on root and two intermediate) and the client certificate in my local machine (win 7 professional) using certmgr.exe.

1. For the web service

  • I have the client certificate (der).
  • The service will be consumed via a .NET proxy.

Here's the code:

OrderWSService proxy = new OrderWSService();
string CertFile = "ClientCert_DER.cer";

proxy.ClientCertificates.Add(new System.Security.Cryptography.X509Certificates.X509Certificate(CertFile));
orderTrackingTO ot = new orderTrackingTO() { order_id = "80", tracking_id = "82", status = stateOrderType.IN_PREPARATION };
resultResponseTO res = proxy.insertOrderTracking(ot);

Exception reported at last statement: The request failed with an empty response.

2. For the HTTP interface

  • it is a HTTPS interface I have to call through POST method.
  • The HTTPS request will be send from a .NET client using HTTPWebRequest.

Here's the code:

string PostData = "MyPostData";

//setting the request
HttpWebRequest req;
req = (HttpWebRequest)HttpWebRequest.Create(url);
req.UserAgent = "MyUserAgent";
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ClientCertificates.Add(new System.Security.Cryptography.X509Certificates.X509Certificate(CertFile, "MyPassword")); 

//setting the request content
byte[] byteArray = Encoding.UTF8.GetBytes(PostData);
Stream dataStream = req.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();

//obtaining the response
WebResponse res = req.GetResponse();
r = new StreamReader(res.GetResponseStream());

Exception reported at last statement: The request was aborted: Could not create SSL/TLS secure channel.

3. Last try: using the browser

In Chrome, after installing the certificates, if I try to access both urls I get a 107 error:

Error 107 (net::ERR_SSL_PROTOCOL_ERROR)

I am stuck.

Answer

David Martin picture David Martin · Jun 27, 2012

The following should help you identify the issue, here are two methods to test SSL connectivity one tests the site whilst the other is a callback method to identify why SSL failed. If nothing else it should give you a better idea why it is failing.

When the method is called it will pop up with the select certificate dialog box, obviously when you do this for real you'll want to read from the cert store automatically. The reason I have put this in is because if no valid certificate is found then you will know your problem is with the way the certificate is installed.

The best thing to do is put this code in a simple console app:

using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Net;

private static void CheckSite(string url, string method)
{
    X509Certificate2 cert = null;
    ServicePointManager.ServerCertificateValidationCallback += ValidateRemoteCertificate;

    X509Store store = new X509Store(StoreLocation.LocalMachine);
    store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
    X509Certificate2Collection certcollection = (X509Certificate2Collection)store.Certificates;
    // pick a certificate from the store
    cert = X509Certificate2UI.SelectFromCollection(certcollection, 
            "Caption",
            "Message", X509SelectionFlag.SingleSelection)[0];

    store.Close();

    HttpWebRequest ws = (HttpWebRequest)WebRequest.Create(url);
    ws.Credentials = CredentialCache.DefaultCredentials;
    ws.Method = method;
    if (cert != null)
        ws.ClientCertificates.Add(cert);

    using (HttpWebResponse webResponse = (HttpWebResponse)ws.GetResponse())
    {
        using (Stream responseStream = webResponse.GetResponseStream())
        {
            using (StreamReader responseStreamReader = new StreamReader(responseStream, true))
            {
                string response = responseStreamReader.ReadToEnd();
                Console.WriteLine(response);
                responseStreamReader.Close();
            }

            responseStream.Close();
        }
        webResponse.Close();
    }
}

/// <summary>
/// Certificate validation callback.
/// </summary>
private static bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
{
    // If the certificate is a valid, signed certificate, return true.
    if (error == System.Net.Security.SslPolicyErrors.None)
    {
        return true;
    }

    Console.WriteLine("X509Certificate [{0}] Policy Error: '{1}'",
        cert.Subject,
        error.ToString());

    return false;
}