【问题标题】:Make Https call using HttpClient使用 HttpClient 进行 Https 调用
【发布时间】:2014-04-10 16:40:22
【问题描述】:

我一直在使用HttpClient 使用 C# 进行 WebApi 调用。与WebClient 相比,它看起来简洁而快速。但是我在拨打Https 电话时遇到了困难。

如何使用以下代码进行Https 调用?

HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://foobar.com/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/xml"));

var task = httpClient.PostAsXmlAsync<DeviceRequest>(
                "api/SaveData", request);

编辑 1: 上面的代码可以很好地进行 http 调用。但是当我将方案更改为 https 时,它不起作用。以下是得到的错误:

底层连接已关闭:无法建立信任 SSL/TLS 安全通道的关系。

编辑 2: 将方案更改为 https 是:第一步。

如何提供证书和公钥/私钥以及 C# 请求。

【问题讨论】:

  • 你只是通过指定new Uri("https://foobar.com/");进行https调用
  • 我很困惑。那还不行吗?你有错误吗? (编辑:在 OP 将 URI 从 https 更改为 http 之前发布)

标签: c# asp.net-web-api dotnet-httpclient


【解决方案1】:

如果服务器仅支持更高的 TLS 版本,例如 TLS 1.2,它仍然会失败,除非您的客户端 PC 默认配置为使用更高的 TLS 版本。要解决这个问题,请在代码中添加以下内容:

System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

修改您的代码示例,它将是

HttpClient httpClient = new HttpClient();   

//specify to use TLS 1.2 as default connection
System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

httpClient.BaseAddress = new Uri("https://foobar.com/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
    
var task = httpClient.PostAsXmlAsync<DeviceRequest>("api/SaveData", request);

【讨论】:

  • @DerekS 很高兴,不客气。如果由于某种原因,您无法修改生产设置中的代码但能够在服务器上进行一些管理,我使用此实用程序将 TLS 1.2 配置为默认值。 nartac.com/Products/IISCrypto
  • SecurityProtocolType.Tls12 找不到你提到的那些枚举值
  • @JobaDiniz 使用 .NET 4.5 或更高版本并包含 System.Net 命名空间。
  • 设置SecurityProtocol是否只需要发生一次?就像在应用程序启动时一样?
  • 您不必使用 .Net 4.5 或更高版本,尽管这样做更容易。您也可以只找到 Tls1.2 的枚举值然后进行显式转换,比如说它是 3,然后在
【解决方案2】:

只需在 URI 中指定 HTTPS。

new Uri("https://foobar.com/");

Foobar.com 将需要一个受信任的 SSL 证书,否则您的调用将因不受信任的错误而失败。

编辑答案:ClientCertificates with HttpClient

WebRequestHandler handler = new WebRequestHandler();
X509Certificate2 certificate = GetMyX509Certificate();
handler.ClientCertificates.Add(certificate);
HttpClient client = new HttpClient(handler);

编辑答案 2:如果您连接的服务器已禁用 SSL、TLS 1.0 和 1.1,并且您仍在运行 .NET Framework 4.5(或更低版本),您需要做出选择

  1. 升级到 .Net 4.6+ (Supports TLS 1.2 by default)
  2. 添加注册表更改以指示 4.5 通过 TLS1.2 进行连接(请参阅:salesforce writeup 以了解要更改的兼容性和密钥或结帐 IISCrypto 请参阅 Ronald Ramos answer comments
  3. 添加应用程序代码以手动配置 .NET 以通过 TLS1.2 进行连接(请参阅 Ronald Ramos answer

【讨论】:

  • @felickz 很好的回应。是否有任何等效于 Windows Phone 8 的 WebRequestHandler 库?
  • @Billatron 用于 WP / Win8 的不同库 .. see
  • 在开发或处理自签名证书时,您可以通过以下方式忽略不受信任的证书错误:ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) =&gt; true;
  • 什么是GetMyX509Certificate
  • @developer 只希望代码不会滑入您的生产构建中 :)
【解决方案3】:

HttpClientHandler 级别存在非全局设置:

var handler = new HttpClientHandler()
{
    SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls
};

var client = new HttpClient(handler);

这样可以启用最新的 TLS 版本。

请注意,默认值 SslProtocols.Default 实际上是 SslProtocols.Ssl3 | SslProtocols.Tls(检查 .Net Core 2.1 和 .Net Framework 4.7.1)。

更新:在 .Net 5.0 中,HttpClientHandler.SslProtocols 的默认值为 None,其含义如下(参见 docs):

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

【讨论】:

  • 感谢您的回答。一点说明:根据本文档:docs.microsoft.com/en-us/dotnet/api/… 它至少需要框架 4.7.1。
  • @GELR 是的,你是对的。上述代码在 .Net 4.6.1 中产生运行时错误。修正了答案。
  • 在 .NET Framework 的早期版本中,此属性是私有的
  • 在 Net Core 3.1 上,这对我有用。设置全局 System.Net.ServicePointManager.SecurityProtocol = xxx - 对数据包跟踪的影响绝对为零。
【解决方案4】:

你的代码应该这样修改:

httpClient.BaseAddress = new Uri("https://foobar.com/");

您只需使用https: URI 方案。 在 MSDN 上有一个有用的页面 here 关于安全 HTTP 连接。确实:

使用 https: URI 方案

HTTP 协议定义了两种 URI 方案:

http : 用于未加密的连接。

https :用于应加密的安全连接。此选项还使用数字证书和证书颁发机构来验证服务器是否是它声称的身份。

此外,请考虑 HTTPS 连接使用 SSL 证书。确保您的安全连接具有此证书,否则请求将失败。

编辑:

以上代码可以很好地进行 http 调用。但是当我改变 方案到https它不起作用,让我发布错误。

什么意思?请求失败?抛出异常?澄清你的问题。

如果请求失败,那么问题应该是 SSL 证书。

要解决此问题,您可以使用类HttpWebRequest,然后使用其属性ClientCertificate。 此外,您还可以找到 here 一个有用的示例,了解如何使用证书发出 HTTPS 请求。

示例如下(如之前链接的 MSDN 页面所示):

//You must change the path to point to your .cer file location. 
X509Certificate Cert = X509Certificate.CreateFromCertFile("C:\\mycert.cer");
// Handle any certificate errors on the certificate from the server.
ServicePointManager.CertificatePolicy = new CertPolicy();
// You must change the URL to point to your Web server.
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://YourServer/sample.asp");
Request.ClientCertificates.Add(Cert);
Request.UserAgent = "Client Cert Sample";
Request.Method = "GET";
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();

【讨论】:

  • 准确地说,诀窍在于将证书与请求一起发送。该怎么做?
  • 链接指向 404 页面 :(
【解决方案5】:

连接https的时候也出现这个错误,我在HttpClient httpClient = new HttpClient();前面加上这行,连接成功:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

我从This AnswerAnother Similar Anwser 知道,评论提到:

这是一个在开发中很有用的 hack,所以在它周围加上一个#if DEBUG #endif 语句是你应该做的最少的事情,以使它更安全并阻止它最终在生产中结束

此外,我没有尝试Another Answer 中使用new X509Certificate()new X509Certificate2() 制作证书的方法,我不确定简单地由new() 创建是否有效。

编辑:一些参考:

Create a Self-Signed Server Certificate in IIS 7

Import and Export SSL Certificates in IIS 7

Convert .pfx to .cer

Best practices for using ServerCertificateValidationCallback

我发现指纹的值等于x509certificate.GetCertHashString()

Retrieve the Thumbprint of a Certificate

【讨论】:

    【解决方案6】:

    我在连接到 GitHub 时遇到了同样的问题,这需要用户代理。因此,提供此内容而不是生成证书就足够了

    var client = new HttpClient();
    
    client.BaseAddress = new Uri("https://api.github.com");
    client.DefaultRequestHeaders.Add(
        "Authorization",
        "token 123456789307d8c1d138ddb0848ede028ed30567");
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json"));
    client.DefaultRequestHeaders.Add(
        "User-Agent",
        "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
    

    【讨论】:

    • 你应该撤销你与互联网共享的那个令牌,我认为 GitHub 可能已经自动完成了。
    • 你真的认为我的令牌以123456789 开头吗??
    【解决方案7】:

    只需在 URI 中指定 HTTPS 即可。

    httpClient.BaseAddress = new Uri("https://foobar.com/");
    

    如果请求使用 HTTP 但使用 HTTPS 失败,则 这肯定是证书问题。确保调用者信任证书颁发者并且证书未过期。一种快速简便的检查方法是尝试在浏览器中进行查询。

    您可能还想检查服务器(如果它是您的和/或如果可以的话)它是否设置为正确处理 HTTPS 请求。

    【讨论】:

      【解决方案8】:

      我也收到了错误:

      底层连接已关闭:无法建立信任 SSL/TLS 安全通道的关系。

      ... 以 Xamarin Forms Android 为目标应用程序尝试从需要 TLS 1.3 的 API 提供程序请求资源。

      解决方案是更新项目配置以换出 Xamarin“托管”(.NET) http 客户端(从 Xamarin Forms v2.5 开始不支持 TLS 1.3),而是使用 android 本机客户端。

      这是 Visual Studio 中的一个简单项目切换。请看下面的截图。

      • 项目属性
      • Android 选项
      • 高级
      • 列表项
      • 将“HttpClient 实现”更改为“Android”
      • 将 SSL/TLS 实施更改为“Native TLS 1.2+”

      【讨论】:

        【解决方案9】:

        我遇到了这个问题,就我而言,解决方案非常简单:以管理员权限打开 Visual Studio。我尝试了上述所有解决方案,但直到我这样做才奏效。希望它可以节省一些宝贵的时间。

        【讨论】:

        • 由于您是该网站的新手,您可能不知道答案旁边是否有绿色勾号,这意味着它已被 OP 接受为解决他/她问题的正确解决方案.除非你能给出一个更好、更完整的答案,否则最好不要提交另一个答案,尤其是在这样一个老问题上。
        • 好吧,我也遇到了这个问题,绿色检查的答案根本没有帮助。以为我提供了一个对我有帮助的替代解决方案,但是,嘿,我下次不会这样做。感谢您的减分;)祝您有美好的一天。
        【解决方案10】:

        将以下声明添加到您的类中:

        public const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
        public const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
        

        之后:

        var client = new HttpClient();
        

        还有:

        ServicePointManager.SecurityProtocol = Tls12;
        System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 /*| SecurityProtocolType.Tls */| Tls12;
        

        开心吗? :)

        【讨论】:

          【解决方案11】:

          你可以尝试使用ModernHttpClient Nuget包:下载包后,你可以这样实现:

           var handler = new ModernHttpClient.NativeMessageHandler()
           {
               UseProxy = true,
           };
          
          
           handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
           handler.PreAuthenticate = true;
           HttpClient client = new HttpClient(handler);
          

          【讨论】:

          • 不幸的是,这对我不起作用。解决方案是通过从 xamarin 托管的 http 客户端切换到 android-native http 客户端来启用 TLS 1.3。请参阅下面的答案。
          【解决方案12】:

          我同意 felickz,但我还想添加一个示例来阐明 c# 中的用法。我在 Windows 服务中使用 SSL 如下。

              var certificatePath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "bin");
              gateway = new GatewayService();
              gateway.PreAuthenticate = true;
          
          
              X509Certificate2 cert = new X509Certificate2(certificatePath + @"\Attached\my_certificate.pfx","certificate_password");
              gateway.ClientCertificates.Add(cert);
          
              ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
              gateway.UserAgent = Guid.NewGuid().ToString();
              gateway.Timeout = int.MaxValue;
          

          如果我要在 Web 应用程序中使用它,我只需像这样更改代理端的实现:

          public partial class GatewayService : System.Web.Services.Protocols.SoapHttpClientProtocol // to => Microsoft.Web.Services2.WebServicesClientProtocol
          

          【讨论】:

            【解决方案13】:

            对于错误:

            底层连接已关闭:无法建立信任 SSL/TLS 安全通道的关系。

            我认为您需要使用以下代码无条件接受证书

            ServicePointManager.ServerCertificateValidationCallback += 
                (sender, cert, chain, sslPolicyErrors) => true;
            

            正如Oppositional 在他对问题.NET client connecting to ssl Web API 的回答中所写。

            【讨论】:

            • 这是绕过证书验证。永远不要在生产环境中这样做。
            • 好吧@JohnKorsnes,如果Abhijeet 正在访问一些公共WebAPI 服务器,我认为他无能为力。这是我的情况。
            猜你喜欢
            • 2019-12-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多