1

This is more about how to get HttpWebRequest to work or even if HttpWebRequest is the right implementation. I've let my C# and .Net skill lapse the past few year, so I hope I can be forgiven for that.

I trying to hit a secure web service that requires client authentication. I have four certs to hit this with.

• Root Certificate • Intermediate Root Certificate • Device Certificate • Private Key

The server is Java and these certs are in .jks form trustore and keystore. I pulled them into .pem files.

So, I failed on the C# client side, so I thought I'd write a little Python snippet to make sure at least the server side is working as expected. Twenty minutes later, I'm making secure posts. Here's that code:

# Keys
path = "C:\\path\\"
key = path + "device.pem"
privkey = path + "device_privkey.pem"
CACerts = path + "truststore.concat" # root & intermediate cert


def post():
    url = "/url"
    headers = {'Content-Type': 'application/xml'}

    ## This section is HTTPSConnection

    context = ssl.SSLContext(ssl.PROTOCOL_TLS)
    context.verify_mode = ssl.CERT_OPTIONAL

    context.load_cert_chain(key, privkey, password='password')
    context.verify_mode = ssl.CERT_NONE
    context.load_verify_locations(CACerts)

    conn = http.client.HTTPSConnection(host, port=8080, context=context)
    conn.request("POST", url, registrationBody, headers)

    response = conn.getresponse()

    regresp = response.read()

The concat certificate is the concatenation of the root and intermediate certificates.

Are you with me?

Now to my C#/.Net headache.

This my attempt. I clearly don't know what I'm doing here.

    public async Task POSTSecure(string pathname, string body)
    {
        string path = "C:\\path";
        string key = path + "device.pem";
        string privkey = path + "device_privkey.pem";
        string CACerts1 = path + "vtn_root.pem";
        string CACerts2 = path + "vtn_int.pem";

        try
        {
            // Create certs from files
            X509Certificate2 keyCert = new X509Certificate2(key);
            X509Certificate2 rootCert = new X509Certificate2(CACerts1);
            X509Certificate2 intCert = new X509Certificate2(CACerts2);

            HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create("https://" + host + ":" + port + pathname);
            ServicePoint currentServicePoint = request.ServicePoint;

            // build the client chain?
            request.ClientCertificates.Add(keyCert);
            request.ClientCertificates.Add(rootCert);
            request.ClientCertificates.Add(intCert);

            Console.WriteLine("URI: {0}", currentServicePoint.Address);

            // This validates the server regardless of whether it should
            request.ServerCertificateValidationCallback = ValidateServerCertificate;

            request.Method = "POST";
            request.ContentType = "application/xml";
            request.ContentLength = body.Length;

            using (var sendStream = request.GetRequestStream())
            {
                sendStream.Write(Encoding.UTF8.GetBytes(body), 0, body.Length);
            }

            var response = (HttpWebResponse)request.GetResponse();
        }
        catch (Exception e)
        {
            Console.WriteLine("Post error.");
        }
    }

Thanks for any help or a pointer to a decent tutorial.

[Edit] More info. On the server side, the debugging points to an empty client certificate chain. This is right after it reports serverhello done.

Dave Fugelso
  • 23
  • 1
  • 6
  • What exactly is the exception you are getting on your catch block? – jegtugado Oct 14 '16 at 01:04
  • {"The request was aborted: Could not create SSL/TLS secure channel."} I've had it, at times getting past this and then getting an error on the server side. I've been messing with this for two days. – Dave Fugelso Oct 14 '16 at 01:08
  • Have you tried setting up the security protocol? `System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;` because in your python code you have specified `ssl.SSLContext(ssl.PROTOCOL_TLS)`. – jegtugado Oct 14 '16 at 01:19
  • Added it. No luck. I think the thing that is missing is the equivalent of the Python load_cert_chain. – Dave Fugelso Oct 14 '16 at 02:05
  • I'm not an expert with certificates but this [answer](http://stackoverflow.com/a/12807434/6138713) might help you out. It involves [X509Chain Class](https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509chain(v=vs.110).aspx). – jegtugado Oct 14 '16 at 02:50

1 Answers1

1

Okay, I think I was pretty close in the original, but I solved it this way:

            request.ClientCertificates = new X509Certificate2Collection(
                                new X509Certificate2(
                                    truststore,
                                    password));

The "trustore" file is a .p12 containing the certificates listed above. The .p12 truststore can be created from the .jks truststore through keytool and openssl. Lots of info out there on how to do that.

Dave Fugelso
  • 23
  • 1
  • 6