如何使用NSURLConnection与SSL连接不可信的证书?

我有以下简单的代码连接到SSL网页

NSMutableURLRequest *urlRequest=[NSMutableURLRequest requestWithURL:url]; [ NSURLConnection sendSynchronousRequest: urlRequest returningResponse: nil error: &error ]; 

除非证书是自签名的, Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate". 有没有办法将它设置为接受连接(就像在浏览器中,你可以按接受)或绕过它的方式吗?

有一个支持的API来完成这个! 把这样的东西添加到NSURLConnection委托中:

 - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; } - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) if ([trustedHosts containsObject:challenge.protectionSpace.host]) [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; } 

请注意, connection:didReceiveAuthenticationChallenge:可以稍后将消息发送到challenge.sender(很多),必要时向用户提供对话框等。

如果您不愿意(或不能)使用私有API,则有一个名为ASIHTTPRequest的开源(BSD许可证)库,它提供了低层CFNetwork APIs的封装。 他们最近引入了使用自签名或不可信证书与-setValidatesSecureCertificate: API来允许HTTPS connectionsfunction。 如果你不想拉入整个库,你可以使用源代码作为参考来自己实现相同的function。

理想情况下,当iOS应用程序需要接受不可信任的证书时,应该只有两种情况。

schemeA:您已连接到使用自签名证书的testing环境。

场景B:您使用MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.来代理HTTPSstream量MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.代理将返回由自签名CA签名的证书,以便代理能够捕获HTTPSstream量。

生产主机不应该使用不受信任的证书, 原因很明显 。

如果您需要让iOS模拟器接受用于testing目的的不可信证书,强烈build议您不要更改应用程序逻辑,以禁用由NSURLConnection API提供的内置证书validation。 如果这个应用程序在没有移除这个逻辑的情况下向公众发布,那么它将容易受到中间人攻击。

为testing目的而接受不可信证书的推荐方法是将签名证书的证书颁发机构(Certificate Authority,CA)证书导入到iOS模拟器或iOS设备上。 我写了一篇快速的博客文章,演示了如何在iOS模拟器上执行此操作:

使用ios模拟器接受不受信任的证书

NSURLRequest有一个名为setAllowsAnyHTTPSCertificate:forHost:的私有方法setAllowsAnyHTTPSCertificate:forHost:它将完成你想要的。 您可以通过类别在NSURLRequest上定义allowsAnyHTTPSCertificateForHost:方法,并将其设置为您要覆盖的主机返回YES

为了补充接受的答案,为了更好的安全性,您可以将您的服务器证书或您自己的根CA证书添加到钥匙串( https://stackoverflow.com/a/9941559/1432048 ),但是这样做不会使NSURLConnection自动validation您的自签名服务器。 您仍然需要将下面的代码添加到您的NSURLConnection委托,它从Apple示例代码AdvancedURLConnections中复制,并且您需要将两个文件(Credentials.h,Credentials.m)从Apple示例代码添加到您的项目。

 - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; } - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { // if ([trustedHosts containsObject:challenge.protectionSpace.host]) OSStatus err; NSURLProtectionSpace * protectionSpace; SecTrustRef trust; SecTrustResultType trustResult; BOOL trusted; protectionSpace = [challenge protectionSpace]; assert(protectionSpace != nil); trust = [protectionSpace serverTrust]; assert(trust != NULL); err = SecTrustEvaluate(trust, &trustResult); trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified)); // If that fails, apply our certificates as anchors and see if that helps. // // It's perfectly acceptable to apply all of our certificates to the SecTrust // object, and let the SecTrust object sort out the mess. Of course, this assumes // that the user trusts all certificates equally in all situations, which is implicit // in our user interface; you could provide a more sophisticated user interface // to allow the user to trust certain certificates for certain sites and so on). if ( ! trusted ) { err = SecTrustSetAnchorCertificates(trust, (CFArrayRef) [Credentials sharedCredentials].certificates); if (err == noErr) { err = SecTrustEvaluate(trust, &trustResult); } trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified)); } if(trusted) [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; } [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; } 

我无法相信这一点, 但是我发现这个工作非常适合我的需求。 shouldAllowSelfSignedCert是我的BOOLvariables。 只需添加到您的NSURLConnection委托,你应该摇摆在每个连接的基础上快速绕过。

 - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)space { if([[space authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) { if(shouldAllowSelfSignedCert) { return YES; // Self-signed cert will be accepted } else { return NO; // Self-signed cert will be rejected } // Note: it doesn't seem to matter what you return for a proper SSL cert // only self-signed certs } // If no other authentication is required, return NO for everything else // Otherwise maybe YES for NSURLAuthenticationMethodDefault and etc. return NO; } 

在iOS 9中,所有无效或自签名证书的SSL连接都将失败。 这是iOS 9.0或更高版本以及OS X 10.11和更高版本中新的App Transport Securityfunction的默认行为。

您可以通过在NSAppTransportSecurity字典中将NSAppTransportSecurity设置为YES来覆盖Info.plist此行为。 不过,我build议仅为testing目的覆盖此设置。

在这里输入图像描述

有关信息,请参阅此处的 App Transport Technote。

Nathan de Vries发布的类别解决scheme将通过AppStore私有API检查,并且在您没有控制NSUrlConnection对象的情况下非常有用。 一个例子是NSXMLParser ,它将打开您提供的URL,但不公开NSURLRequestNSURLConnection

在iOS 4中,解决方法似乎仍然有效,但是只有在设备上,模拟器不再调用allowsAnyHTTPSCertificateForHost:方法。

你必须使用NSURLConnectionDelegate来允许HTTPS连接,并且iOS8有新的callback。

推荐使用:

 connection:canAuthenticateAgainstProtectionSpace: connection:didCancelAuthenticationChallenge: connection:didReceiveAuthenticationChallenge: 

相反,你需要声明:

connectionShouldUseCredentialStorage: – 发送以确定URL加载器是否应使用凭证存储来authentication连接。

connection:willSendRequestForAuthenticationChallenge: – 告诉委托,连接将发送一个authentication质询请求。

使用willSendRequestForAuthenticationChallenge您可以像使用弃用的方法一样使用challenge ,例如:

 // Trusting and not trusting connection to host: Self-signed certificate [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; 

我发布了一些基本的代码(基于我注意到的其他人的工作),它可以让你正确地对自己生成的证书进行身份validation(以及如何获得免费证书 – 请参阅Cocoanetics的评论底部)

我的代码在这里github

如果你想继续使用sendSynchronousRequest我在这个解决scheme中工作:

 FailCertificateDelegate *fcd=[[FailCertificateDelegate alloc] init]; NSURLConnection *c=[[NSURLConnection alloc] initWithRequest:request delegate:fcd startImmediately:NO]; [c setDelegateQueue:[[NSOperationQueue alloc] init]]; [c start]; NSData *d=[fcd getData]; 

你可以在这里看到: Objective-C SSL同步连接

通过AFNetworking,我已经成功地使用了https webservice和下面的代码,

 NSString *aStrServerUrl = WS_URL; // Initialize AFHTTPRequestOperationManager... AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; manager.requestSerializer = [AFJSONRequestSerializer serializer]; manager.responseSerializer = [AFJSONResponseSerializer serializer]; [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; manager.securityPolicy.allowInvalidCertificates = YES; [manager POST:aStrServerUrl parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { successBlock(operation, responseObject); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { errorBlock(operation, error); }]; 

你可以使用这个代码

 -(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodServerTrust) { [[challenge sender] useCredential:[NSURLCredential credentialForTrust:[[challenge protectionSpace] serverTrust]] forAuthenticationChallenge:challenge]; } } 

使用-connection:willSendRequestForAuthenticationChallenge:而不是这些弃用的方法

推荐使用:

 -(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace -(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge -(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge