强制ASP.NET WebAPI客户端发送客户端证书,即使没有匹配的CA

11

我有一个具体的应用程序需要使用客户端证书来进行HTTPS请求的互相认证。服务器拥有灵活的证书验证策略,可以接受未在服务器证书存储中出现的自签名客户端证书。使用curl作为客户端的情况已知能够正常工作。

通过测试和数据包嗅探,我发现Microsoft的ASP.NET HttpClient 在SSL握手期间试图过度智能化。这个特定的客户端只会在它与服务器信任根之间存在链式信任时才使用WebRequestHandler.ClientCertificates集合中的客户端证书。我的观察结果是,如果没有带有链式信任的证书,那么客户端在握手期间根本不会发送证书。

这是可以理解的默认行为,但过于限制了,在客户端似乎没有办法关闭它。我尝试了各种其他的WebRequestHandler属性,包括AuthenticationLevelClientCertificateOptions,但都没有成功。

是否有一种方法能够强制HttpClient发送客户端证书,即使它似乎在服务器端无法验证?我对直接和反射(hack)的解决方案都持开放态度,因为我确实需要这个客户端能够工作。


你找到解决方案了吗? - Daniel
有关这个问题,您有什么想法吗? - Carsten Schütte
2018年了,这仍然是事实 :( - komsky
2个回答

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/") };

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

-1

我曾经遇到过同样的问题,但是没有成功解决。同时我还观察到了一个奇怪的行为,即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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接