Gianni
Gianni

Reputation: 51

NSURLSession completion handler meaning if didReceiveChallenge is automatically called

Can someone explain me if didReceiveChallenge is automatically called after i make a request to https server with NSURLSession, if the completion handler is calling some internal methods after didReceiveChallenge completes and how i can access this completion handler? the delegate method has the following firm:

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

[EDIT]

Usually i see this method with this basic implementation:

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

}

Upvotes: 0

Views: 2395

Answers (2)

Gianni
Gianni

Reputation: 51

First of all thank you for your time. I will try to better explain what are my requirements. I have two possible cases ANY and SYSTEM_VALIDATED and based on that i should do the following things:

  • ANY: i should allow any certificates
  • SYSTEM_VALIDATED: the framework i am building read a data.bin file from the App client bundle and this file can contain one or more CA(Certificate Authority)(CASE A) that the company which has required this SDK has provided to us or it can contains a simple word 'SYSTEM'(CASE B). In this cases i should do the following things

    • CASE A: compare the certificate/s that i should build from the data.bin file with the public key of the server trust certificate
    • CASE B: compare all the certificates installed on the system with the public key of the server trust certificate

The ANY case if i am not wrong should be done by the following above two lines:

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

CASE A i should make a manual comparison and evaluate the certificates and maybe for CASE B i should do nothing and let the URL Loading System to default evaluate the server trust

I am currently reading this apple docs and this apple docs. I understand that i should make a manual server trust Evaluation for CASE A but fo CASE B and ANY should i call those two lines above or do nothing and let the URL Loading System handling everything?

[EDIT]

I forgot to mention that this request is sent to a server company which support TLS 1.2 with a valid CA certificate.

Upvotes: 0

dgatwood
dgatwood

Reputation: 10417

Before I go any further, I should start by pointing out that doing any of this is almost always a mistake. You can get free TLS certificates from various groups like LetsEncrypt. So unless you have some very unusual use case (such as needing to provide trust for devices that are not connected to the public Internet, communicating via link-local networking), you are almost always better off not doing any of this, and just installing a real TLS certificate on your test server.

With that said....

The URLSession:didReceiveChallenge:completionHandler: method is called on the session's delegate (if non-nil) whenever the OS needs to ask for additional confirmation. It may not be called for every request, but it usually is. It is called for every https request, period, because of server trust evaluation.

The code you have above is probably failing you because you aren't asking for default handling in cases where the protection space is something other than server trust (e.g. proxy authentication, HTTP basic/digest auth, etc.), which means the network machinery just sits there waiting for you to tell it what to do, oblivious to the fact that the block it passed you has been freed when the method returned, and thus will never get called.

You should be doing something like this:

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

Additionally, the code you posted is very unsafe, because you are not doing anything to validate the certificate. See Overriding TLS Chain Validation Correctly in Apple's developer documentation for more information on how to do this step, but typically you do so by either

  • Providing a copy of the root certificate (without the key) used to sign your fake certificate, then adding that root certificate to the set of valid root certs, then reevaluating the certificate.
  • Providing a copy of the public key for the fake certificate, and asserting that it matches the expected key.

With either approach, you should probably fall back to default handling, so that if you ever replace the self-signed certificate with a real one, it will "just work".

Upvotes: 1

Related Questions