关于c#:使用HttpClient进行Https调用

Make Https call using HttpClient

我一直在使用HttpClient使用C#进行WebApi调用。 与WebClient相比,似乎整洁而快速。 但是,我在进行Https通话时遇到问题。

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

1
2
3
4
5
6
7
8
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时,它不起作用。 这是获得的错误:

The underlying connection was closed: Could not establish trust
relationship for the SSL/TLS secure channel.

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

How do I supply certificate & public / private key along with C#
request.


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

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

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

1
2
3
4
5
6
7
8
9
10
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);


只需在URI中指定HTTPS。

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

Foobar.com将需要具有受信任的SSL证书,否则您的呼叫将因不受信任的错误而失败。

编辑答案:ClientCertificates与HttpClient

1
2
3
4
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(或更低版本),则需要做出选择

  • 升级到.Net 4.6+(默认情况下支持TLS 1.2)
  • 添加注册表更改以指示4.5通过TLS1.2进行连接(请参阅:salesforce编写的兼容性和密钥更改或结帐IISCrypto,请参见Ronald Ramos答案)
  • 添加应用程序代码以手动配置.NET以通过TLS1.2进行连接(请参阅Ronald Ramos答案)

  • 您的代码应通过以下方式修改:

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

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

    Use the https: URI scheme

    The HTTP Protocol defines two URI schemes:

    http : Used for unencrypted connections.

    https : Used for secure connections that should be encrypted. This option also uses digital certificates and certificate authorities to verify that the server is who it claims to be.

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

    编辑:

    Above code works fine for making http calls. But when I change the
    scheme to https it does not work, let me post the error.

    这是什么意思?请求失败?抛出异常了吗?澄清您的问题。

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //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();


    当连接到https时,我也收到此错误,我在HttpClient httpClient = new HttpClient();之前添加了这一行并成功连接:

    1
    ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

    我从"这个答案"和"另一个类似的答案"知道这一点,并且评论中提到:

    This is a hack useful in development so putting a #if DEBUG #endif statement around it is the least you should do to make this safer and stop this ending up in production

    此外,我没有尝试使用" new X509Certificate()"或" new X509Certificate2()"制作证书的"其他答案"中的方法,我不确定仅由new()创建即可。

    编辑:一些参考:

    在IIS 7中创建自签名服务器证书

    在IIS 7中导入和导出SSL证书

    将.pfx转换为.cer

    使用ServerCertificateValidationCallback的最佳做法

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

    检索证书的指纹


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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");


    仅在URI中指定HTTPS就可以解决问题。

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

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

    您可能还需要检查服务器(如果是您的服务器和/或(如果可以的话))已将其设置为正确处理HTTPS请求。


    我也收到错误:

    The underlying connection was closed: Could not establish trust
    relationship for the SSL/TLS secure channel.

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

    解决方案是更新项目配置,以换出Xamarin"托管"(.NET)http客户端(该客户端自Xamarin Forms v2.5起不支持TLS 1.3),而改用android native客户端。

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

    • 项目属性
    • Android选项
    • 高级
    • 项目清单
    • 将" HttpClient实现"更改为" Android"
    • 将SSL / TLS实施更改为"本机TLS 1.2+"

    enter image description here


    HttpClientHandler级别存在一个非全局设置:

    1
    2
    3
    4
    5
    6
    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进行了检查)。


    将以下声明添加到您的班级:

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

    后:

    1
    var client = new HttpClient();

    和:

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

    快乐? :)


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        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应用程序中使用它,我只是在代理端更改实现,如下所示:

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

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


    您可以尝试使用ModernHttpClient Nuget软件包:下载该软件包后,可以像这样实现它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
     var handler = new ModernHttpClient.NativeMessageHandler()
     {
         UseProxy = true,
     };


     handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
     handler.PreAuthenticate = true;
     HttpClient client = new HttpClient(handler);


    对于错误:

    The underlying connection was closed: Could not establish trust
    relationship for the SSL/TLS secure channel.

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

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

    正如Oppositional在他对.NET客户端连接到ssl Web API的问题的回答中所写。