HTTPS 请求使用 HttpClient 失败

Posted

技术标签:

【中文标题】HTTPS 请求使用 HttpClient 失败【英文标题】:HTTPS request fails using HttpClient 【发布时间】:2017-08-02 10:32:57 【问题描述】:

我正在使用以下代码并得到HttpRequestException 异常:

using (var handler = new HttpClientHandler())

    handler.ClientCertificateOptions = ClientCertificateOption.Manual;
    handler.SslProtocols = SslProtocols.Tls12;
    handler.ClientCertificates.Add(new X509Certificate2(@"C:\certificates\cert.pfx"));

    // I also tried to add another certificates that was provided to https access 
    // by administrators of the site, but it still doesn't work.
    //handler.ClientCertificates.Add(new X509Certificate2(@"C:\certificates\cert.crt"));
    //handler.ClientCertificates.Add(new X509Certificate2(@"C:\certificates\cert_ca.crt"));

    using (var client = new HttpClient(handler))
    
        var response = client.GetAsync("https://someurl.com/api.php?arg1=some&arg2=test").GetAwaiter().GetResult();
        // ^ HttpRequestException: An error occurred while sending the request.
    

例外:

WinHttpException: A security error occurred
    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult()
    System.Net.Http.WinHttpHandler+<StartRequest>d__105.MoveNext()

HttpRequestException: An error occurred while sending the request.
    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult()
    System.Net.Http.HttpClient+<FinishSendAsync>d__58.MoveNext()
    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
    System.Runtime.CompilerServices.TaskAwaiter.GetResult()
    MyApp.Web.Controllers.HomeController.Test() in HomeController.cs
        var response = client.GetAsync("https://someurl.com/api.php?arg1=some&arg2=test").GetAwaiter().GetResult();
    lambda_method(Closure , object , Object[] )
    Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeActionMethodAsync>d__27.MoveNext()

我还尝试将相同的证书导出到 Windows 证书商店并通过 Google Chrome 使用它,它工作正常(浏览器要求我确认已安装的证书,然后加载资源)。​​

为什么它在我的代码中不起作用?

更新

我还尝试添加回调来验证证书:

handler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) =>

    // I set a breakpoint to this point but it is not catched.
    return true;
;

更新2

证书使用 SHA-1。 Neil Moss 在 support for SHA1 certs is being withdrawn 的 cmets 中被提及。 如果这是它无法正常工作的真正原因,是否有解决方法?

解决方案

感谢 Neil Moss 的解决方案。他建议将 Tls 标志用于 SSL 协议。

handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;

但它还需要以下内容:

handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;

在我添加了这个之后,它就可以正常工作了。

【问题讨论】:

请提供错误的完整详细信息:类型、消息和堆栈跟踪。如果有内部异常,那是什么? 嗨@Richard,我已经用异常消息和堆栈跟踪更新了我的问题。 澄清一下,您是尝试通过 客户端证书 通过 HTTPS 连接,还是尝试包含自签名 服务器证书 您希望将其视为有效吗? 完全题外话:为什么client.GetAsync(…).GetAwaiter().GetResult()?为什么不简单地await client.GetAsync(…) 您的证书是否使用 SHA1 签名?正在撤销对 SHA1 证书的支持 - 可能相关吗? blogs.windows.com/msedgedev/2016/04/29/sha1-deprecation-roadmap 【参考方案1】:

根据this SO post,您必须使用 ServicePointManager 启用 TLS1.2。

System.Net.ServicePointManager.SecurityProtocol |=
    SecurityProtocolType.Tls12 | 
    SecurityProtocolType.Tls11 | 
    SecurityProtocolType.Tls; // comparable to modern browsers

同样值得注意的是,ServicePointManager.SecurityProtocols 属性的 MSDN documentation 做出了这样的声明:

.NET Framework 4.6 包含一项新的安全功能,可阻止 连接的不安全密码和散列算法。

这表明某种形式的 SHA1 块可能就位。

编辑 2020 年 9 月 16 日

我将 = 赋值运算符更改为 |= 运算符,以便对仍然需要 SSL 的任何其他旧网站的请求将继续工作。

【讨论】:

非常感谢您,尼尔·莫斯!在我添加 Tls 协议后,它就可以工作了!但据我所知,没有System.Net.ServicePointManager.SecurityProtocol,但我使用了handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls; 需要注意的是,对于 .net core 2.0,您需要使用 HttpClientHandler 而不是 ServicePointManager。 当我们请求任何 https 网站时,ServicePointManager 会做什么?【参考方案2】:

这是一份非常有用的文件。对于 ASP.NET Core 2.0,答案应用如下(结果成功):

using (var handler = new HttpClientHandler())

    handler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
    handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;
    using (HttpClient client = new HttpClient(handler))
    
        string requestObjJson = requestObj.ToJson();
        var address = new Uri($"https://yourcompany.com/");
        string token = GetToken();
        client.BaseAddress = address;
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        var contentData = new StringContent(requestObjJson, System.Text.Encoding.UTF8, "application/json");
        using (var response = await client.PostAsync("yourcompany/new-employee", contentData))
        
            var content = response.Content.ReadAsStringAsync();
            var taskResult = content.Result;
            JObject resultObj = JObject.Parse(taskResult);
            return resultObj;
        
    

【讨论】:

感谢您提供此代码 sn-p,它可能会提供一些有限的短期帮助。一个正确的解释would greatly improve 其长期价值,通过展示为什么这是解决问题的好方法,并将使其对未来有其他类似问题的读者更有用。请edit您的回答添加一些解释,包括您所做的假设。【参考方案3】:

根据documentation,最好使用None。 无“允许操作系统选择要使用的最佳协议,并阻止不安全的协议。除非您的应用有特定理由不这样做,否则您应该使用此字段”

【讨论】:

以上是关于HTTPS 请求使用 HttpClient 失败的主要内容,如果未能解决你的问题,请参考以下文章

HttpClient 异步请求失败

HttpClient GetAsync 随机失败

HttpClient使用示列(post请求的)

Angular httpClient https调用错误

通用httpclient生成方式

httpclient跳过https请求的验证