使用 Httpclient 信任自签名证书 [英] Trust a self signed certificate using Httpclient

查看:107
本文介绍了使用 Httpclient 信任自签名证书的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm attempting to make a web request that's failing because of a self signed certificate :

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

这会引发信任失败异常.

This throws a trust failure exception.

我再次尝试使用 httpclienthandler 按照此处的建议 Allowing Untrusted使用 HttpClient 的 SSL 证书:

I tried again using httpclienthandler as suggested here 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); 

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

This blows up throwing a system not implemented exception.

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

Are there any other ways to trust a self signed cert? I've even installed the certificate on the machine making the request but no luck.

推荐答案

我看到了很多与此相关的问题,我想我会尽可能写出完整的答案和示例.

I have seen so many question regarding this I figured I write up as a complete answer and example as I can.

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

Note: Using WKWebView with self-sign certs, see this answer

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

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

System.Net.Http.HttpRequestException: An error occurred while sending the request ---> System.Net.WebException: Error: TrustFailure (One or more errors occurred.) ---> System.AggregateException: One or more errors occurred. ---> System.Security.Authentication.AuthenticationException: A call to SSPI failed, see inner exception. ---> Mono.Security.Interface.Tl

原来的 Mono Managed 提供程序正在真正长期坚持,并且在安全性和安全性方面只支持 TLS1.0.性能我会转而使用 NSUrlSession 实现.

The original Mono Managed provider is getting really long in the tooth and only supports TLS1.0, in terms of security & performance I would move to using the NSUrlSession implementation.

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

Note: As this iOS version is fairly old now and I personally do not target it anymore, so I leave this blank... (unless someone really needs me to lookup my notes for it ;-)

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

Xamarin provides a HttpMessageHandler subclass (NSUrlSessionHandler) that is based upon iOS' NSUrlSession.

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

Using it by itself against a self-signed cert will result in:

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

System.Net.WebException: An SSL error has occurred and a secure connection to the server cannot be made. ---> Foundation.NSErrorException: Exception of type 'Foundation.NSErrorException' was thrown.

问题是自签名证书被 iOS 视为不安全且不受信任,因此您必须将 ATS 异常应用于您的应用程序,以便 iOS 知道您的应用程序在 Info.plist<中不受信任./代码>.

The problem is that a self-sign cert is considered insecure and non-trusted by iOS, thus you have to apply an ATS exception to your app so iOS knows that your app is untrusted in the 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 请求现在将导致此错误:

Now that iOS knows that your app is making untrusted calls, a HttpClient request will now result in this error:

System.Net.WebException:此服务器的证书无效.您可能正在连接到一个伪装成self-signed.badssl.com"的服务器,这可能会使您的机密信息处于危险之中.--->Foundation.NSErrorException:抛出了Foundation.NSErrorException"类型的异常.

System.Net.WebException: The certificate for this server is invalid. You might be connecting to a server that is pretending to be‚ self-signed.badssl.com‚ which could put your confidential information at risk. ---> Foundation.NSErrorException: Exception of type 'Foundation.NSErrorException' was thrown.

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

This error is due to the fact that even though the ATS exception has been allow, the default NSUrlSession provided by iOS will apply its standard NSUrlAuthenticationChallenge to the certificate and fail since a self-signed cert can never be truly authenticated (even via client pinning) since it does not include a root certificate authority (CA) in its chain that is trusted by iOS.

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

Thus you need to intercept and bypass the certificate security checking provided by iOS (Yes, a big security alert, flashing red lights, etc...)

但是,您可以通过创建一个执行绕过的 NSUrlSessionDataDelegate 子类来做到这一点.

But, you can do this via creating a NSUrlSessionDataDelegate subclass that does the bypass.

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 的构造函数.

Now you need to apply that NSUrlSessionDataDelegate to a NSUrlSession and use that new session in the creation of your NSUrlSessionHandler that will be provided in the constructor of the 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 并将其重新用于您的所有请求(即单例模式)

Note: Example only, normally you would create a single Delegate, NSUrlSession, HttpClient, NSUrlSessionHandler and re-use it for all your requests (i.e. Singleton pattern)

您的请求现在有效:

<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 的选项确实是新的(11 月2017),目前还没有发布版本(alpha、beta 或稳定版),但当然,源代码位于:

Note: The option to supply your own custom NSUrlSession to Xamarin's NSUrlSessionHandler is really new (Nov. 2017) and not currently in a release build (alpha, beta or stable), but of course, source is available at:

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

You can also directly use a NSUrlSession instead of HttpClient against a self-signed cert.

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 并将其重新用于您的所有请求,即单例模式

Note: Example only, normally you would create a single Delegate and NSUrlSession and re-use it for all your requests, i.e. Singleton pattern

IHMO,一起避免自签名证书,即使在开发环境中,并使用其中一种免费证书服务,避免应用 ATS 异常、自定义代码拦截/绕过 iOS 安全性等的所有麻烦……和让您的应用网络服务真正安全.

IHMO, avoid self-signed certs all together, even in a development environment and use one of the free certificate services and avoid all the headaches of applying ATS exceptions, custom code to intercept/bypass iOS security, etc... and make your app web services actually secure.

我个人使用 Let's Encrypt:

I personally use Let’s Encrypt:

这篇关于使用 Httpclient 信任自签名证书的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆