Gimly
Gimly

Reputation: 6175

HttpClient reused with client certificate

I've read at several places (like here, here or here) that it's a bad practice to dispose of the HttpClient directly after a request and it's better to dispose of it after all the request have been made, to allow reuse of the connection.

To try that out, I've created an instance of HttpClient and added to a static field in my class this way:

public class Test
{
    private static X509Certificate2 _certificate;
    private static HttpClient HttpClient { get; set; }

    ...

    public Test()
    {
        ...

        if (HttpClient == null)
        {
            LoadCertificate();

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
                                                    | SecurityProtocolType.Tls11
                                                    | SecurityProtocolType.Tls12
                                                    | SecurityProtocolType.Ssl3;

            var handler = new WebRequestHandler();
            handler.ClientCertificates.Add(_certificate);
            HttpClient = new HttpClient(handler, false);
        }
    }

    private void LoadCertificate()
    {
        using (var store = new X509Store(StoreName.My, CertificateStoreLocation))
        {
            store.Open(OpenFlags.ReadOnly);
            var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, CertificateFriendlyName, true);
            if (certificates.Count != 1)
                throw new ArgumentException(
                    $"Cannot find a valid certificate with name {CertificateFriendlyName} in {CertificateStoreLocation}");
            _certificate = certificates[0];

            store.Close();
        }

        ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
    }

}

I'm then using my instance to call a web service through this command:

var result = await HttpClient.PostAsJsonAsync(completeUri, request);

The first time I'm running the code, everything works fine and I get a response correctly, but then, all the following time I get an unauthorized from the server telling me that I didn't use a client certificate.

It's like if for the following calls, the WebRequestHandler wasn't took into consideration.

Upvotes: 1

Views: 1870

Answers (1)

Serge Semenov
Serge Semenov

Reputation: 10052

Your fix should look like this:

handler.PreAuthenticate = true;

Once you establish a connection to a service, you can re-use it to communicate with it using different clients with different auth information. That means, the service needs to know which client sent a request each time, otherwise it could be a security breach - e.g. executing a request under last connected client. It depends on your authentication mechanism, but basically WebRequestHandler sets flag IsAuthenticated after the first request, and stops sending the auth information on next requests. The PreAuthenticate options forces to send the auth info on every request.

Upvotes: 1

Related Questions