使用HttpClient进行Https调用


157

我一直在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#请求。


2
您只需指定即可进行https调用new Uri("https://foobar.com/");
felickz 2014年

2
我很困惑。那还行吗?您遇到错误了吗?(编辑:在OP将URI从https更改为http之前发布)
2014年

Answers:


215

如果服务器仅支持更高的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

1
@JobaDiniz使用.NET 4.5或更高版本,并包含System.Net命名空间。
罗纳德·拉莫斯

设置SecurityProtocol是否只需要发生一次?像在应用程序启动时一样?
CamHart

这很好。谢谢。我还发现我不需要清除使用此HttpClient client = new HttpClient();的任何标头。ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; HttpResponseMessage response =等待client.GetAsync(“ https:// ...”);
AtLeastTheresToast

127

只需在URI中指定HTTPS。

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

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

编辑答案:ClientCertificates与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+(默认情况下支持TLS 1.2
  2. 添加注册表更改以指示4.5通过TLS1.2进行连接(请参阅:salesforce编写的兼容性和密钥以更改或结帐IISCrypto参见Ronald Ramos 回答评论
  3. 添加应用程序代码以手动配置.NET以通过TLS1.2进行连接(请参阅Ronald Ramos 答复

@felickz很好的回应。Windows Phone 8有与WebRequestHandler库等效的功能吗?
Billatron 2014年

@Billatron用于WP / Win8的不同库.. 参见
felickz 2014年

6
在开发或处理自签名证书时,可以使用以下命令忽略不受信任的证书错误: ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
开发人员

6
什么GetMyX509Certificate
Fandi Susanto

6
@developer只希望代码不会进入您的生产版本中:)
felickz

15

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

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

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

使用https:URI方案

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

http:用于未加密的连接。

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

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

编辑:

上面的代码可以很好地进行http调用。但是,当我将方案更改为https时,它不起作用,请允许我发布错误。

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

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

要解决此问题,可以使用类HttpWebRequest,然后使用其property 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 2014年

在哪里获取“ C:\\ mycert.cer”?如何生成mycert.cer文件?
masiboo

@masiboo我写的内容回答了如何进行HTTPS调用。向Google询问如何生成* .cer文件,您将自己找到答案。
阿尔贝托·索拉诺'18

9

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

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

我从《答案》和《另一个相似的答案》中知道这一点,评论中提到:

这在开发中很有用,因此在其中添加#if DEBUG #endif语句至少是使该代码更安全并停止在生产中使用的最少方法。

此外,我没有尝试使用“ 其他答案”中使用new X509Certificate()new X509Certificate2()制作证书的方法,我不确定简单地创建by new()是否会起作用。

编辑: 一些参考:

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

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

将.pfx转换为.cer

使用ServerCertificateValidationCallback的最佳做法

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

检索证书的指纹


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

2
您应该撤销与互联网共享的令牌,我认为GitHub可能会自动完成。
Amias

21
您真的认为我的令牌以123456789?? 开头吗?
罗五世丹戈

5

在以下级别存在一个非全局设置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)。


感谢你的回答。注意事项:根据此文档:docs.microsoft.com/zh-cn/dotnet/api/…它至少需要框架4.7.1。
GELR

@GELR是的,您是对的。上面的代码在.Net 4.6.1中产生运行时错误。固定答案。
停止

在.NET Framework的
早期

在Net Core 3.1上,这一切对我有用。设置全局System.Net.ServicePointManager.SecurityProtocol = xxx-在数据包跟踪中的影响绝对为零。
罗恩·史密斯

3

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

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

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

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


:我有一个类似的问题:我在浏览器中进行了查询,并且证书有效(呼叫通过,被授权为服务级别,并且所有的工作都像超级按钮一样),但是在编程上不起作用。还有什么呢?是否使用从浏览器“手动调用服务”获得的证书与从Web应用程序以编程方式调用服务获得的证书完全相同?
卡洛斯(Carlos)

2

我也收到错误:

基础连接已关闭:无法为SSL / TLS安全通道建立信任关系。

...使用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+”

在此处输入图片说明


1

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

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;

快乐?:)


1

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


由于您是该网站的新手,因此您可能不知道答案旁边是否有绿色的对勾,这意味着OP接受了该答案作为其问题的正确解决方案。除非您能给出更好,更完整的答案,否则最好不要提交其他答案,尤其是在这么老的问题上。
山姆W

4
好吧,我也遇到了这个问题,绿色选中的答案根本没有帮助。以为我给出了一个对我有帮助的替代解决方案,但是,嘿,下次我不会这样做。谢谢你的减分;)祝你有美好的一天。
Lucian Satmarean

0

您可以尝试使用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。请参阅下面的答案。
MSC

0

我同意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

-4

对于错误:

基础连接已关闭:无法为SSL / TLS安全通道建立信任关系。

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

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

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


4
这绕过了证书验证。永远不要在生产中这样做。
约翰·科尼斯

好吧,@ JohnKorsnes,如果Abhijeet正在访问某个公共WebAPI服务器,我认为他不能为此做很多事情。这是我的情况。
NemanjaSimović
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.