305

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

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

除了如果证书是自签名证书它会给出错误Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".有没有办法将它设置为无论如何都接受连接(就像在浏览器中你可以按接受一样)或绕过它的方法?

4

13 回答 13

418

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

于 2010-01-09T15:21:35.553 回答
36

如果您不愿意(或无法)使用私有 API,则有一个名为ASIHTTPRequest的开源(BSD 许可)库,它提供了对较低级别CFNetwork APIs. 他们最近引入了允许通过APIHTTPS connections使用自签名或不受信任的证书的功能。-setValidatesSecureCertificate:如果您不想引入整个库,您可以使用源代码作为参考来自己实现相同的功能。

于 2009-06-10T01:19:25.187 回答
33

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

场景 A:您连接到使用自签名证书的测试环境。

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

出于显而易见的原因,生产主机不应使用不受信任的证书。

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

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

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

于 2011-08-11T14:36:56.883 回答
12

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

于 2009-06-01T10:53:41.077 回答
11

我不能对此表示赞赏,但我发现这个非常适合我的需求。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;
}
于 2010-02-02T19:50:59.320 回答
11

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

- (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];
}
于 2014-02-07T07:15:14.080 回答
10

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

您可以通过在字典中Info.plist设置NSAllowsArbitraryLoads为来覆盖 , 中的此行为。但是,我建议仅出于测试目的覆盖此设置。YESNSAppTransportSecurity

在此处输入图像描述

有关信息,请参阅此处的应用程序传输技术说明。

于 2015-08-24T19:05:53.410 回答
7

Nathan de Vries 发布的类别解决方法将通过 AppStore 私有 API 检查,并且在您无法控制NSUrlConnection对象的情况下很有用。一个示例是NSXMLParser打开您提供的 URL,但不公开NSURLRequestor NSURLConnection

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

于 2010-07-21T12:58:10.427 回答
6

您必须使用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];
于 2015-02-13T00:46:34.363 回答
3

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

我的代码在这里github

于 2012-03-16T23:25:34.003 回答
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 同步连接

于 2013-09-17T07:36:34.387 回答
2

您可以使用此代码

-(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
于 2017-01-07T12:31:23.627 回答
1

使用AFNetworking,我已成功使用以下代码使用 https 网络服务,

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);
}];
于 2015-09-07T09:37:55.773 回答