I would like to ask a followup question to a previously posed question. I've got the code to create an NSURLRequest/Connection, run it and have the callback methods for authentication get called. Here's the specific code:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] || [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodDefault];
}
-(void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge previousFailureCount] > 0) {
[[challenge sender] cancelAuthenticationChallenge:challenge];
NSLog(@"Bad Username Or Password");
badUsernameAndPassword = YES;
finished = YES;
return;
}
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
if (appDelegate._allowInvalidCert)
{
// Go ahead...trust me!
[challenge.sender useCredential:
[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]
forAuthenticationChallenge: challenge];
}
else
{
TrustGenerator *tg = [[TrustGenerator alloc] init];
if ([tg getTrust:challenge.protectionSpace])
{
// Go ahead...trust me!
[challenge.sender useCredential:
[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]
forAuthenticationChallenge: challenge];
}
else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
}
else if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodDefault) {
NSURLCredential *newCredential = [NSURLCredential credentialWithUser:_username password:_password persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
}
}
What I'm running into is that "didReceiveAuthenticationChallenge" with "[challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]" is ALWAYS being called, even when the certificate on the server I'm attempting to connect to is trusted (doing testing with a Verisign cert). So what I'm seeing is my application is always prompting the end user to trust even when the website is trusted. Bad karma considering that's what's suppose to happen with a man in the middle attack, etc. What I'm really looking for is some code like this:
if (appDelegate._allowInvalidCert)
{
// Go ahead...trust me!
[challenge.sender useCredential:
[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]
forAuthenticationChallenge: challenge];
}
else if(The OS trusts the cert on the server)
{
[challenge.sender useCredential:
[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]
forAuthenticationChallenge: challenge];
}
else{...
So I spent a few days researching this. It looks like while the NSURLConnection API cannot determine if a certificate is trusted, there's a method in the Security Framework that handels that. So here's the code I came up with:
-(void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge previousFailureCount] > 0) {
[[challenge sender] cancelAuthenticationChallenge:challenge];
NSLog(@"Bad Username Or Password");
badUsernameAndPassword = YES;
finished = YES;
return;
}
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
SecTrustResultType result;
//This takes the serverTrust object and checkes it against your keychain
SecTrustEvaluate(challenge.protectionSpace.serverTrust, &result);
if (appDelegate._allowInvalidCert)
{
[challenge.sender useCredential:
[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]
forAuthenticationChallenge: challenge];
}
//When testing this against a trusted server I got kSecTrustResultUnspecified every time. But the other two match the description of a trusted server
else if(result == kSecTrustResultProceed || result == kSecTrustResultConfirm || result == kSecTrustResultUnspecified){
[challenge.sender useCredential:
[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]
forAuthenticationChallenge: challenge];
}
else
{
//Asks the user for trust
TrustGenerator *tg = [[TrustGenerator alloc] init];
if ([tg getTrust:challenge.protectionSpace])
{
//May need to add a method to add serverTrust to the keychain like Firefox's "Add Excpetion"
[challenge.sender useCredential:
[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]
forAuthenticationChallenge: challenge];
}
else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
}
else if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodDefault) {
NSURLCredential *newCredential = [NSURLCredential credentialWithUser:_username password:_password persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
}
}
If the result is kSecTrustResultConfirm
, you should actually ask the user if it's a trusted server.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With