Reputation: 51
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
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 certificatesSYSTEM_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
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
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
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