使用HttpClient进行Https调用

193

我一直在使用C#中的HttpClient来进行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# 请求中提供证书和公钥/私钥。


2
你只需指定 new Uri("https://foobar.com/"); 就可以进行 HTTPS 调用。 - felickz
2
我有点困惑。那不是已经可以工作了吗?你收到了错误吗?(编辑:在 OP 将 URI 从 https 更改为 http 之前发布) - Tim
14个回答

267
如果服务器仅支持更高版本的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配置为默认设置。https://www.nartac.com/Products/IISCrypto - Ronald Ramos
无法找到您提到的枚举值:SecurityProtocolType.Tls12。 - JobaDiniz
1
@JobaDiniz 使用 .NET 4.5 或更高版本,并包括 System.Net 命名空间。 - Ronald Ramos
这很好用。谢谢。我还发现我不需要清除任何标头,只需使用以下代码:HttpClient client = new HttpClient(); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; HttpResponseMessage response = await client.GetAsync("https://..."); - AtLeastTheresToast
3
尽管使用 .Net 4.5 或更高版本会更容易,但你并不需要这样做。你也可以查找 Tls1.2 的枚举值,然后进行显式转换,例如它的值是3,那么在< .Net 4.5 中,你可以执行 *.SecurityProtocol = (SecurityProtocolType)3;,它会起作用。.net 4.5+仍在运行4.0运行时,只是你正在使用的库版本没有定义该枚举值,但它仍然存在于框架中。 - Mike
显示剩余3条评论

133

只需在URI中指定HTTPS。

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

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

编辑回答:使用HttpClient的ClientCertificates

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

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

  1. 升级到.Net 4.6+ (默认支持TLS 1.2)
  2. 添加注册表更改以指示4.5通过TLS1.2进行连接(参见:Salesforce文档了解兼容性和要更改的键,或者查看IISCrypto,请参阅Ronald Ramos的回答评论
  3. 添加应用程序代码以手动配置.NET以通过TLS1.2进行连接(请参阅Ronald Ramos的回答

7
在开发或处理自签名证书时,您可以使用以下代码忽略不受信任的证书错误:ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; - developer
11
GetMyX509Certificate是什么?(意为询问该函数或方法的定义或作用) - Fandi Susanto
@FandiSusanto 另一个话题... 请看 - felickz
6
@开发者 希望那段代码不会意外地进入你的生产版本构建中 :) - felickz
如果使用客户端证书,将不会调用ServerCertificateValidationCallback。参见:https://github.com/dotnet/corefx/issues/17226 - Jari Turkia
显示剩余5条评论

25

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,这意味着以下情况(请参见文档):

允许操作系统选择最佳协议并阻止不安全的协议。除非您的应用程序有特定原因不使用此字段,否则应使用它。


感谢您的回答。小提示:根据此文档: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.sslprotocols?view=netframework-4.8 它要求至少使用框架4.7.1。 - GELR
@GELR 是的,你说得对。上面的代码在 .Net 4.6.1 中会产生运行时错误。已经修正了答案。 - stop-cran
1
在之前的.NET Framework版本中,此属性是私有的。 - JotaBe
在Net Core 3.1上,以下是我所使用的全部有效方法。在数据包跟踪中,设置全局System.Net.ServicePointManager.SecurityProtocol = xxx - 完全没有任何效果。 - Rowan Smith

15

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

当连接到https时出现错误,我在HttpClient httpClient = new HttpClient();之前添加以下代码,并成功连接:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

我知道这个方法是从这个答案另一个类似的答案中了解到的。并且评论提到:

这是一种在开发中有用的黑客方法,因此在其周围放置#if DEBUG #endif语句是至少应该做的,以使其更安全,防止其进入生产环境。

此外,我没有尝试另一个答案中使用new X509Certificate()new X509Certificate2()创建证书的方法,我不确定仅仅通过new()创建会不会起作用。

编辑: 一些参考资料:

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

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

将.pfx转换为.cer

使用ServerCertificateValidationCallback的最佳实践

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

检索证书的Thumbprint


15

你的代码需要这样修改:

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

你只需要使用 https: URI 方案。这里有一个在 MSDN 上关于安全 HTTP 连接的有用页面(链接)

使用 https: URI 方案

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

http:用于未加密连接。

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

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

编辑:

上面的代码在进行 http 调用时可以正常工作。但是,当我把方案更改为 https 时,它就无法工作了,让我贴出错误信息。

不起作用是什么意思?请求失败了吗?抛出异常了吗?请澄清你的问题。

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

要解决此问题,您可以使用类HttpWebRequest,然后使用其属性ClientCertificate。 此外,您可以在(链接)中找到一个有用的示例,演示如何使用证书进行 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();

准确地说,诀窍在于将证书与请求一起发送。如何做到这一点? - Abhijeet
链接导致了一个404页面 :( - Ddddan

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

4
你应该撤销你一直在与互联网共享的那个令牌,我认为GitHub可能已经自动执行了此操作。 - Amias
35
你真的认为我的记号以 123456789 开头吗? - Carlo V. Dango

3

只需在URI中指定HTTPS即可。

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

如果请求使用HTTP可以正常工作,但是使用HTTPS失败了,那么这很可能是证书问题。请确保调用者信任证书颁发机构并且证书没有过期。一个快速简便的检查方法就是在浏览器中尝试进行查询。
另外,您可能还需要检查服务器上(如果是您自己的服务器或您有权限),是否设置为正确地处理HTTPS请求。

3

我遇到了这个问题,但是解决方案非常简单:以管理员权限打开Visual Studio。我尝试了上面提供的所有解决方案,但直到我以管理员身份运行后才成功。希望这能为大家节省宝贵的时间。


由于您是该网站的新用户,您可能不知道如果一个答案旁边有绿色的勾选标志,那就意味着它被提问者接受为解决他/她问题的正确答案。除非您能给出更好、更完整的答案,否则最好不要提交另一个答案,特别是在这样一个老问题上。 - Sam W
14
我也遇到了同样的问题,但是绿色勾选的答案一点都没帮助我。我想提供一个帮助我的备选方案,但是下次我不会这么做了。感谢你给我减分,祝你有愉快的一天。 - Lucian Satmarean

2
我也遇到了这个错误:
基础连接已关闭: 无法建立 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+"
屏幕截图如下:(注意:保留HTML标签) enter image description here

0
请在您的类中添加以下声明:
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;

开心吗?:)


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