使用 Httpclient 信任自签名证书

Posted

技术标签:

【中文标题】使用 Httpclient 信任自签名证书【英文标题】:Trust a self signed certificate using Httpclient 【发布时间】:2017-12-05 23:11:42 【问题描述】:

我正在尝试发出由于自签名证书而失败的网络请求:

Client = new HttpClient(); 
HttpResponseMessage Response = await Client.GetAsync(Uri)//defined elsewhere 

这会引发信任失败异常。

我再次尝试使用httpclienthandler,这里建议Allowing Untrusted SSL Certificates with HttpClient:

 var handler = new HttpClientHandler();

 handler.ServerCertificateCustomValidationCallback = 
 (
   HttpRequestMessage message, 
   X509Certificate2 cert, 
   X509Chain chain, 
   SslPolicyErrors errors
  ) =>return true; ;//remove if this makes it to production 

  Client = new HttpClient(handler); 

这会引发系统未实现的异常。

还有其他方法可以信任自签名证书吗?我什至在发出请求的机器上安装了证书,但没有运气。

【问题讨论】:

这是在什么平台上运行的? 这是针对 ios 使用 xamarin/c# 你在用什么HttpClientImplementation 我不明白你在问什么,我刚刚开始使用不是 php 的东西,我正在使用从 Visual Studios 的 nugent 包管理器安装的System.net.Http跨度> HttpClient 实现标记为managed (default) 【参考方案1】:

我已经看到了很多关于这个的问题,我想我会尽可能地写一个完整的答案和例子。

注意:将WKWebView 与自签名证书一起使用,请参阅此answer

HttpClient 实现

注意:在本例中使用badssl.com

托管(默认)

System.Net.Http.HttpRequestException: 发送请求时出错---> System.Net.WebException:错误:TrustFailure(发生一个或多个错误。)---> System.AggregateException:发生一个或多个错误。 ---> System.Security.Authentication.AuthenticationException:对 SSPI 的调用失败,请参阅内部异常。 ---> Mono.Security.Interface.Tl

原来的 Mono Managed 提供程序已经真的在使用了很长时间,并且仅支持 TLS1.0,就安全性和性能而言,我将转向使用 NSUrlSession 实现。

CFNetwork (iOS 6+)

注意:由于这个 iOS 版本现在已经相当老了,我个人不再针对它,所以我把这个留空......(除非有人真的需要我查找我的笔记;-)

NSUrlSession (iOS 7+)

Xamarin 提供了一个基于 iOS 的 NSUrlSessionHttpMessageHandler 子类 (NSUrlSessionHandler)。

对自签名证书单独使用它会导致:

System.Net.WebException:发生 SSL 错误,无法与服务器建立安全连接。 ---> Foundation.NSErrorException: 引发了“Foundation.NSErrorException”类型的异常。

问题在于自签名证书被 iOS 视为不安全且不受信任,因此您必须对您的应用应用 ATS 例外,以便 iOS 知道您的应用在 Info.plist 中不受信任。

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>self-signed.badssl.com</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
    </dict>
</dict>

现在 iOS 知道您的应用正在进行不受信任的调用,HttpClient 请求现在将导致此错误:

System.Net.WebException:此服务器的证书无效。您可能正在连接到一个伪装成 self-signed.badssl.com 的服务器,这可能会使您的机密信息面临风险。 ---> Foundation.NSErrorException:引发了“Foundation.NSErrorException”类型的异常。

此错误是由于即使允许 ATS 异常,iOS 提供的 默认 NSUrlSession 会将其标准 NSUrlAuthenticationChallenge 应用于证书并由于 self -signed 证书永远无法真正进行身份验证(即使通过客户端固定),因为它的链中不包含 iOS 信任的根证书颁发机构 (CA)。

因此你需要拦截和绕过 iOS 提供的证书安全检查(是的,一个大的安全警报,闪烁的红灯等......)

但是,您可以通过创建一个用于绕过的NSUrlSessionDataDelegate 子类来做到这一点。

public class SelfSignedSessionDataDelegate : NSUrlSessionDataDelegate, INSUrlSessionDelegate

    const string host = "self-signed.badssl.com";
    public override void DidReceiveChallenge(NSUrlSession session, NSUrlAuthenticationChallenge challenge, Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
    
        switch (challenge.ProtectionSpace.Host)
        
            case host:
                using (var cred = NSUrlCredential.FromTrust(challenge.ProtectionSpace.ServerSecTrust))
                
                    completionHandler.Invoke(NSUrlSessionAuthChallengeDisposition.UseCredential, cred);
                
                break;
            default:
                completionHandler.Invoke(NSUrlSessionAuthChallengeDisposition.PerformDefaultHandling, null);
                break;
        
    

现在您需要将该NSUrlSessionDataDelegate 应用到NSUrlSession,并在创建NSUrlSessionHandler 时使用该新会话,该会话将在HttpClient 的构造函数中提供。

var url = "https://self-signed.badssl.com";
using (var selfSignedDelegate = new SelfSignedSessionDataDelegate())
using (var session = NSUrlSession.FromConfiguration(NSUrlSession.SharedSession.Configuration, (INSUrlSessionDelegate)selfSignedDelegate, NSOperationQueue.MainQueue))
using (var handler = new NSUrlSessionHandler(session))
using (var httpClient = new HttpClient(handler))
using (var response = await httpClient.GetAsync(url))
using (var content = response.Content)

    var result = await content.ReadAsStringAsync();
    Console.WriteLine(result);

注意:仅作为示例,通常您会创建一个单一的 Delegate、NSUrlSession、HttpClient、NSUrlSessionHandler 并将其重新用于您的所有请求(即单例模式)

您的请求现在有效:

<html>
   <head>
    <title>self-signed.badssl.com</title>
  </head>
  <body><div id="content"><h1 style="font-size: 12vw;">
    self-signed.<br>badssl.com
    </h1></div>
  </body>
</html>

注意:向 Xamarin 的 NSUrlSessionHandler 提供您自己的自定义 NSUrlSession 的选项确实是(2017 年 11 月),目前不在发布版本中( alpha、beta 或 stable),但当然,源代码可在以下位置获得:

xamarin-macios/src/Foundation/NSUrlSessionHandler.cs

使用NSUrlSession 代替HttpClient

您也可以针对自签名证书直接使用NSUrlSession 而不是HttpClient

var url = "https://self-signed.badssl.com";
using (var selfSignedDelegate = new SelfSignedSessionDataDelegate())
using (var session = NSUrlSession.FromConfiguration(NSUrlSession.SharedSession.Configuration, (INSUrlSessionDelegate)selfSignedDelegate, NSOperationQueue.MainQueue))

    var request = await session.CreateDataTaskAsync(new NSUrl(url));
    var cSharpString = NSString.FromData(request.Data, NSStringEncoding.UTF8).ToString(); 
    Console.WriteLine(cSharpString);

注意:仅作为示例,通常您会创建一个 Delegate 和 NSUrlSession 并将其重新用于您的所有请求,即单例模式

真正的解决方案?使用免费安全证书:

IHMO,避免一起使用自签名证书,即使是在开发环境中,并使用免费证书服务之一,避免应用 ATS 异常、拦截/绕过 iOS 安全性的自定义代码等所有令人头疼的问题......以及让您的应用网络服务真正安全。

我个人使用 Let's Encrypt:

https://letsencrypt.org

【讨论】:

谢谢你或深入写下这篇文章!这对我非常有益! @Kisaragi 很高兴它有帮助 ? 感谢您的来信!从字面上看,这是我唯一能找到的。但是,我有一些问题:可用的源代码很棒,但我们应该如何在实际应用程序中使用它?对我们来说,我们不是试图绕过自签名证书,而是进行证书固定。 @ThomasF。您是否尝试通过客户端固定证书使用WKWebView 除非我有什么误解,否则似乎有一个新属性NSUrlSessionHandler.TrustOverrideForUrl 可能比创建委托更方便,但可以达到相同的目标。

以上是关于使用 Httpclient 信任自签名证书的主要内容,如果未能解决你的问题,请参考以下文章

HttpClient 使用自签名证书调用 HTTPS WebApi

自签名SSL证书是啥?有用吗

自签名SSL证书是啥?有用吗?

在 ktor httpClient(js) JS 引擎中忽略自签名证书的配置

信任来自 IIS 的自签名证书

HttpClient 拒绝发送自签名客户端证书