HttpClient 与客户端证书重用

HttpClient reused with client certificate

我在几个地方(比如 here, here or here)读到,在请求后直接处理 HttpClient 是一种不好的做法,最好在所有请求都发出后处理它,以允许重用连接。

为了尝试一下,我创建了一个 HttpClient 实例,并以这种方式添加到我的 class 中的静态字段中:

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; };
    }

}

然后我使用我的实例通过此命令调用 Web 服务:

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

我第一次使用 运行 代码时,一切正常,我得到了正确的响应,但是接下来的所有时间我都从服务器收到未经授权的消息,告诉我我没有使用客户端证书。

就好像在接下来的调用中,WebRequestHandler 没有被考虑在内。

您的修复应该如下所示:

handler.PreAuthenticate = true;

一旦您建立了与服务的连接,您就可以重新使用它,使用具有不同身份验证信息的不同客户端与其进行通信。这意味着,该服务需要知道每次哪个客户端发送请求,否则可能是安全漏洞 - 例如在上次连接的客户端下执行请求。这取决于您的身份验证机制,但基本上 WebRequestHandler 在第一个请求后设置标志 IsAuthenticated,并在下一个请求时停止发送身份验证信息。 PreAuthenticate 选项强制在每次请求时发送授权信息。