使用 NSURLConnection 和 NSURLProtectionSpace 确定信任
Posted
技术标签:
【中文标题】使用 NSURLConnection 和 NSURLProtectionSpace 确定信任【英文标题】:Determining Trust With NSURLConnection and NSURLProtectionSpace 【发布时间】:2011-06-29 23:47:21 【问题描述】:我想向 a previously posed question 提出后续问题。我有创建 NSURLRequest/Connection 的代码,运行它并调用用于身份验证的回调方法。具体代码如下:
- (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];
我遇到的是带有“[challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]”的“didReceiveAuthenticationChallenge”总是被调用,即使我正在尝试服务器上的证书连接到是受信任的(使用 Verisign 证书进行测试)。所以我看到的是我的应用程序总是提示最终用户信任,即使网站是受信任的。考虑到这是一个人在中间攻击时会发生的事情,等等。我真正想要的是这样的代码:
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...
【问题讨论】:
好帖子!我在哪里可以找到TrustGenerator
代码?
【参考方案1】:
所以我花了几天时间研究这个。看起来虽然 NSURLConnection API 无法确定证书是否可信,但安全框架中有一个方法可以处理它。所以这是我想出的代码:
-(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];
【讨论】:
+[NSURLCredential credentialForTrust:]
创建一个只为会话保留的凭据,因此它不会像 Firefox 中的附加异常一样。一旦你的应用被杀掉,你就必须重新信任它。
我知道这是一篇旧帖子,但我只是注意到 kSecTrustResultConfirm
已被弃用。【参考方案2】:
如果结果是kSecTrustResultConfirm
,您实际上应该询问用户它是否是受信任的服务器。
【讨论】:
【参考方案3】:如果您拥有 CA 可信证书,上述答案才有效,因为在这种情况下,您使用的是苹果允许的 CA 证书进行验证。
如果你有自签名证书,你应该使用你自己的 CA 服务器证书来检查它是否有效......
我发现了一个很好的(有点混乱)here。它也涵盖了两次握手....
希望对你有所帮助!
【讨论】:
以上是关于使用 NSURLConnection 和 NSURLProtectionSpace 确定信任的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 iOS 7 的 NSURLSession 接受自签名 SSL 证书
使用NSURLConnection和NSURLProtectionSpace确定信任
使用 NSURLConnection 和 NSURLProtectionSpace 确定信任
使用 NSAutoreleasePool 和 NSURLConnection