Dave Fugelso
Dave Fugelso

Reputation: 23

.Net client authentication setup using HttpWebRequest

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.

Upvotes: 1

Views: 3374

Answers (1)

Dave Fugelso
Dave Fugelso

Reputation: 23

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.

Upvotes: 1

Related Questions