代码之家  ›  专栏  ›  技术社区  ›  Kisaragi

使用Httpclient信任自签名证书

  •  7
  • Kisaragi  · 技术社区  · 7 年前

    我正试图发出一个由于自签名证书而失败的web请求:

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

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

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

    1 回复  |  直到 7 年前
        1
  •  13
  •   SushiHangover    7 年前

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

    注意:使用 WKWebView 使用自签名证书,请参见此 answer

    HttpClient实现

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

    托管(默认)

    系统网Http。HttpRequestException:发送请求时出错---> 系统网WebException:错误:TrustFailure(发生了一个或多个错误。)---&燃气轮机; 系统AggregateException:发生了一个或多个错误---&燃气轮机; 系统安全身份验证。AuthenticationException:调用SSPI失败,请参阅内部异常---&燃气轮机; 单声道。安全界面Tl

    原始单声道 Managed 提供商正在获取 真正地 长在牙齿上,仅支持TLS1.0,在安全和;性能我将转而使用NSUrlSession实现。

    CFNetwork(iOS 6以上)

    注意:由于这个iOS版本现在已经相当旧了,我个人不再瞄准它,所以我将此留空。。。(除非有人真的需要我查找笔记;-)

    NSUrlSession(iOS 7以上)

    Xamarin提供了 HttpMessageHandler 子类( NSUrlSessionHandler )这是基于iOS的 NSUrlSession .

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

    系统网WebException:出现SSL错误,无法与服务器建立安全连接---&燃气轮机; 地基N错误异常: “基础”类型的例外。引发了“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 请求现在将导致此错误:

    系统网WebException:此服务器的证书无效。您可能正在连接一个假装是自签名的服务器。Badsl。com–这可能会使您的机密信息处于风险之中---&燃气轮机; 地基N错误异常:“Foundation”类型的异常。引发了“NSErrorException”。

    此错误是由于即使允许ATS异常,但 违约 NSUrlSession iOS提供的将应用其标准 NSUrlAuthenticationChallenge 由于自签名证书永远无法进行真正的身份验证(即使通过客户端固定),因为它在其链中不包括受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 到a 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);
    }
    

    注: 仅举个例子,通常您会创建一个委托、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>
    

    注: 提供您自己定制的选项 NSUrlSession 到Xamarin的 NSUrlSessionHandler 真的是 (2017年11月),目前尚未发布版本(alpha、beta或stable),但当然,可从以下网址获取来源:

    使用 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);
    }
    

    注: 仅举个例子,通常您会创建一个委托和NSUrlSession,并将其重新用于所有请求,即单例模式

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

    IHMO,避免所有自签名证书,甚至在开发环境中,使用免费证书服务之一,避免应用ATS异常、自定义代码拦截/绕过iOS安全等所有令人头痛的事情。。。并使你的应用程序web服务真正安全。

    我个人使用Lets加密: