如何使用NSURLConnection连接SSL以获取不受信任的证书?

时间:2009-06-01 02:09:35

标签: ios objective-c https ssl-certificate app-transport-security

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

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

如果证书是自签名的Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".,则会出现错误。是否有办法将其设置为接受连接(就像在浏览器中按下接受一样)或绕过它的方法?

13 个答案:

答案 0 :(得分:414)

有一个支持的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等等。

答案 1 :(得分:35)

如果您不愿意(或无法)使用私有API,那么有一个名为ASIHTTPRequest的开源(BSD许可证)库,它提供了一个围绕较低级别CFNetwork APIs的包装器。他们最近推出了允许HTTPS connections使用-setValidatesSecureCertificate: API的自签名或不受信任证书的功能。如果您不想拉入整个库,可以使用源作为参考,自己实现相同的功能。

答案 2 :(得分:33)

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

方案A:您已连接到使用自签名证书的测试环境。

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

生产主机永远不应使用obvious reasons的不受信任的证书。

如果您需要让iOS模拟器接受不受信任的证书以进行测试,强烈建议您不要更改应用程序逻辑,以禁用NSURLConnection API提供的内置证书验证。如果应用程序在不删除此逻辑的情况下向公众发布,则它将容易受到中间人攻击。

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

accepting untrusted certificates using the ios simulator

答案 3 :(得分:12)

NSURLRequest有一个名为setAllowsAnyHTTPSCertificate:forHost:的私有方法,它会完全按照您的意愿执行。您可以通过类别在allowsAnyHTTPSCertificateForHost:上定义NSURLRequest方法,并将其设置为为您要覆盖的主机返回YES

答案 4 :(得分:11)

为了补充已接受的答案,为了更好的安全性,您可以将服务器证书或您自己的根CA证书添加到钥匙串(https://stackoverflow.com/a/9941559/1432048),但是单独执行此操作不会使NSURLConnection对您的自签名进行身份验证服务器自动。您仍然需要将以下代码添加到NSURLConnection委托中,它是从Apple示例代码AdvancedURLConnections复制的,您需要将两个文件(Credentials.h,Credentials.m)从apple示例代码添加到您的项目中。 / p>

- (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];
}

答案 5 :(得分:10)

我不能相信这一点,but this one I found非常适合我的需要。 shouldAllowSelfSignedCert是我的BOOL变量。只需添加到您的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;
}

答案 6 :(得分:10)

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

您可以在Info.plist字典中将NSAllowsArbitraryLoads设置为YES,从而覆盖此行为。 但是,我建议仅将此设置覆盖以用于测试目的。

enter image description here

有关信息,请参阅App Transport Technote here

答案 7 :(得分:6)

Nathan de Vries发布的类别解决方法将通过AppStore私有API检查,在您无法控制NSUrlConnection对象的情况下非常有用。 一个示例是NSXMLParser,它会打开您提供的网址,但不会公开NSURLRequestNSURLConnection

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

答案 8 :(得分:5)

您必须使用NSURLConnectionDelegate来允许HTTPS连接,并且iOS8会有新的回调。

<强>推荐使用:

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

相反,你需要声明:

  

connectionShouldUseCredentialStorage: - 发送以确定URL加载程序是否应使用凭据存储来验证连接。

     

connection:willSendRequestForAuthenticationChallenge: - 告知代理该连接将发送身份验证质询请求。

使用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];

答案 9 :(得分:2)

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

我的代码在github

答案 10 :(得分:2)

如果您想继续使用 sendSynchronousRequest ,我可以使用此解决方案:

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 Synchronous Connection

答案 11 :(得分:1)

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

答案 12 :(得分:0)

您可以使用此代码

-(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