11

我有一个特定的应用程序,它需要使用客户端证书来对 HTTPS 请求进行相互身份验证。服务器具有灵活的证书验证策略,允许它接受服务器证书存储中不存在的自签名客户端证书。众所周知,使用 curl 作为客户端可以正常工作。

我通过测试和数据包嗅探确定的是,Microsoft 的 ASP.NETHttpClient在 SSL 握手期间试图过于智能。WebRequestHandler.ClientCertificates如果此特定客户端具有与服务器的受信任根之一的信任链,则该特定客户端将仅使用客户端证书(来自集合)。我观察到的是,如果没有具有信任链的证书,那么客户端在握手期间根本不会发送证书。

这是可以理解的默认行为,但过于严格,似乎没有办法将其关闭。我已经尝试过各种其他WebRequestHandler属性,包括AuthenticationLevelClientCertificateOptions无济于事。

有没有办法HttpClient在集合中可用时强制发送客户端证书ClientCertificates,即使它似乎不会在服务器端验证?我对直接和肮脏(反射黑客)的解决方案持开放态度,因为我真的需要这个客户才能工作。

4

2 回答 2

0

我有同样的问题,同样没有运气。还观察到一个奇怪的行为,当 WebRequesthandler 有时确实发送证书时,这取决于线程/AppPool 凭据。

我设法通过用 RestClient 替换 HttpClient 来解决这个问题。RestClient 是开源的,可通过 nuget 获得。

RestSharp 页面

API 非常相似,并且无需抱怨证书即可完成所需的魔法:

        var restClient = new RestClient($"{serviceBaseUrl}{requestUrl}");
        X509Certificate certificate = GetCertificateFromSomewhere();
        restClient.ClientCertificates = new X509CertificateCollection { certificate };
        var request = new RestRequest(Method.POST);
        request.RequestFormat = DataFormat.Json;
        request.AddParameter(new Parameter()
        {
            Type = ParameterType.RequestBody,
            Name = "Body",
            ContentType = "application/json",
            Value = "{your_json_body}"
        });

        IRestResponse<T> response = client.Execute<T>(request);
        if (response.ErrorException != null)
        {
            throw new Exception(response.Content, response.ErrorException);
        }
        return response.Data;
于 2018-08-01T16:38:52.613 回答
0

我认为您有多个证书或需要多个证书并需要附加它们。您可以向 X509CertificateCollection 添加尽可能多的证书。其中之一必须与 https 服务器证书匹配,否则您无法调用 Web 服务。

try
    {
        X509Certificate2 clientCert = GetClientCertificate("cert1");
         X509Certificate2 clientCert = GetClientCertificate("cert2");
        X509Certificate2 clientCert = GetClientCertificate("cert3");
        WebRequestHandler requestHandler = new WebRequestHandler();
        requestHandler.ClientCertificates.Add(clientCert1);
         requestHandler.ClientCertificates.Add(clientCert2);

requestHandler.ClientCertificates.Add(clientCert3); HttpClient client = new HttpClient(requestHandler) { BaseAddress = new Uri(" http://localhost:3020/ ") };

        HttpResponseMessage response = client.GetAsync("customers").Result;
        response.EnsureSuccessStatusCode();
        string responseContent = response.Content.ReadAsStringAsync().Result;
        Console.WriteLine(responseContent);     
    }
    catch (Exception ex)
    {
        Console.WriteLine("Exception while executing the test code: {0}", ex.Message);
    }

然后调用这个请求。

private static X509Certificate2 GetClientCertificate( string probablerightcert)
{
    X509Store userCaStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
    try
    {
        userCaStore.Open(OpenFlags.ReadOnly);
        X509Certificate2Collection certificatesInStore = userCaStore.Certificates;
        X509Certificate2Collection findResult = certificatesInStore.Find(X509FindType.FindBySubjectName, probablerightcert, true);
        X509Certificate2 clientCertificate = null;
        if (findResult.Count == 1)
        {
            clientCertificate = findResult[0];
        }
        else
        {
            throw new Exception("Unable to locate the correct client certificate.");
        }
        return clientCertificate;
    }
    catch
    {
        throw;
    }
    finally
    {
        userCaStore.Close();
    }
}
于 2020-05-22T14:26:25.307 回答