Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two way authentication with HTTPClient

I am trying to make HTTP calls to a server that requires a two-way SSL connection (client authentication). I have a .p12 file that contains more than one certificate and a password. Request is serialized using protocol buffer.

My first thought was to add the keystore to the ClientCertificate properties of the WebRequestHandler used by the HttpClient. I've also added the keystore to my trusted root Certification Authorities on my computer.

I'm always getting a "could not create ssl/tls secure channel" when the PostAsync is executed. There's obviously something that I do wrong but I'm a bit at loss here.

Any pointers would be appreciated.

    public void SendRequest()
    {
        try
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

            var handler = new WebRequestHandler();

            // Certificate is located in bin/debug folder
            var certificate = new X509Certificate2Collection();
            certificate.Import("MY_KEYSTORE.p12", "PASSWORD", X509KeyStorageFlags.DefaultKeySet);

            handler.ClientCertificates.AddRange(certificate);
            handler.ServerCertificateValidationCallback = ValidateServerCertificate;

            var client = new HttpClient(handler)
            {
                BaseAddress = new Uri("SERVER_URL")
            };
            client.DefaultRequestHeaders.Add("Accept", "application/x-protobuf");
            client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-protobuf");
            client.Timeout = new TimeSpan(0, 5, 0);

            // Serialize protocol buffer payload
            byte[] protoRequest;
            using (var ms = new MemoryStream())
            {
                Serializer.Serialize(ms, MyPayloadObject());
                protoRequest = ms.ToArray();
            }

            var result = await client.PostAsync("/resource", new ByteArrayContent(protoRequest));

            if (!result.IsSuccessStatusCode)
            {
                var stringContent = result.Content.ReadAsStringAsync().Result;
                if (stringContent != null)
                {
                    Console.WriteLine("Request Content: " + stringContent);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            throw;
        }
   }

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

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

            // Do not allow this client to communicate with unauthenticated servers.
            return false;
        }

EDIT

I don't even break into ValidateServerCertificate. Exception is thrown as soon as the PostAsync is called. Protocol is definitely TLS v1.

Client OS is Windows 8.1. Server is coded in Java (not sure on what OS it's running on. I don't have access to it. It's a black box.)

Stacktrace

at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context) at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)

There's no inner exception.

like image 291
Mathieu Avatar asked Sep 24 '15 20:09

Mathieu


1 Answers

Did you try changing your security protocol to Ssl3? Either case you would need to set Expect property to true. It will fix your error. Further you can explore this link to get more knowledge over passing client certificate for authentication.

public void SendRequest()
{
    try
    {
        ServicePointManager.Expect100Continue = true;
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;

        var handler = new WebRequestHandler();
        .....
    }
    ..
}
like image 95
Amnesh Goel Avatar answered Oct 11 '22 04:10

Amnesh Goel