NSURLSession完成处理程序的含义是是否didReceiveChallenge被自动调用

时间:2018-11-29 21:04:35

标签: ios nsurlsession nsurlrequest

有人可以向我解释如果我使用didReceiveChallenge向https服务器发出请求后是否自动调用NSURLSession,如果完成处理程序在didReceiveChallenge完成后正在调用某些内部方法以及我如何可以访问此完成处理程序?委托方法具有以下内容:

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler

[编辑]

通常,我会在基本实现中看到此方法:

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
  if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
  {
    if([challenge.protectionSpace.host
        isEqualToString:@"google.it"])
    {
      NSURLCredential *credential = [NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust];
      completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
    }
    else
      completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
  }

}

2 个答案:

答案 0 :(得分:0)

在继续之前,我应该指出执行其中任何一项几乎总是一个错误。您可以从LetsEncrypt之类的各个组获取免费的TLS证书。因此,除非您有一些非常不寻常的用例(例如需要为未连接到公共Internet的设备提供信任,通过链接本地网络进行通信),否则您通常总是只需在您的测试服务器上安装一个真正的TLS证书即可。

话虽如此...

只要操作系统需要进行其他确认,就会在会话的委托人(如果非nil)上调用URLSession:didReceiveChallenge:completionHandler:方法。可能不会为每个请求都调用它,但通常是这样。由于服务器信任评估,因此在每个https请求期间都调用它。

上面的代码可能会让您失败,因为在保护空间不是服务器信任(例如代理身份验证,HTTP基本/摘要身份验证等)的情况下,您不要求默认处理。网络机器只是坐在那里等着你告诉它该怎么做,而忽略了当方法返回时释放通过它的块的事实,因此将永远不会被调用。

您应该这样做:

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
  if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
  {
    if([challenge.protectionSpace.host isEqualToString:@"google.it"])
    {
      if (/* Manually verify the certificate here in some way */) {
        NSURLCredential *credential = [NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
      } else {
        // Evaluation failed.  Reject the certificate.
        completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
      }
      // Do not fall through for either case above.
      return;
    }
  }
  completionHandler(NSURLSes NSURLSessionAuthChallengePerformDefaultHandling, nil);
}

此外,您发布的代码非常不安全,因为您没有做任何事情来验证证书。有关如何执行此步骤的更多信息,请参见Apple开发人员文档中的Overriding TLS Chain Validation Correctly,但通常您可以选择其中一种进行

  • 提供用于签名假证书的根证书的副本(不带密钥),然后将该根证书添加到一组有效的根证书中,然后重新评估该证书。
  • 提供伪造证书的公共密钥副本,并断言它与期望的密钥匹配。

无论采用哪种方法,您都应该回到默认处理方式,这样,如果您用真实的证书替换了自签名证书,它将“起作用”。

答案 1 :(得分:0)

首先感谢您的宝贵时间。我将尝试更好地解释我的要求。我有两种可能的情况ANYSYSTEM_VALIDATED,因此我应该做以下事情:

  • ANY:我应该允许所有证书
  • SYSTEM_VALIDATED:我正在构建的框架从App客户端捆绑中读取了一个data.bin文件,并且该文件可以包含该公司拥有的一个或多个CA(证书颁发机构)(案例A)该SDK已提供给我们的要求,也可以包含一个简单的单词“ SYSTEM”(案例B)。在这种情况下,我应该做以下事情

    • 案例A:将我应该从data.bin文件中构建的证书与服务器信任证书的公钥进行比较
    • 案例B:将系统上安装的所有证书与服务器信任证书的公钥进行比较

如果我没记错的话,应该通过以下两行完成:

NSURLCredential *credential = [NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust];
    completionHandler(NSURLSessionAuthChallengeUseCredential,credential);   

案例A我应该进行手动比较并评估证书,也许对于案例B我应该什么都不做,让URL加载系统默认评估服务器信任度

我目前正在阅读此apple docs和此apple docs。我知道我应该对案例A进行手动服务器信任评估,但是对于案例B和任何其他案例,我应该在上面的这两行中调用,还是什么都不做,让URL加载系统处理所有事情?

[编辑]

我忘了提到此请求已发送给服务器公司,该公司使用有效的CA证书支持TLS 1.2。