0

我很难过:我的应用程序需要连接到使用 HTTPS 的自签名证书并需要客户端身份验证的服务器。更糟糕的是,我实际上需要 iOS 媒体播放器来连接到该服务器,所以我已经完全按照 Apple 的说明进行操作:

credential = [NSURLCredential credentialWithIdentity:identity certificates:certs persistence:NSURLCredentialPersistenceForSession];
NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:@"server.com" 
                                                                    port:0
                                                                protocol:NSURLProtectionSpaceHTTPS 
                                                                   realm:nil 
                                                    authenticationMethod:NSURLAuthenticationMethodClientCertificate];
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:space];

但它只是行不通。所以我尝试手动向服务器发出请求:

NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"https://server.com"]
                                       completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                           NSLog(@"Done : %@", error ? error : @"OK");
                                       }];

我得到的只是这个错误:

2016-06-13 08:22:37.767 TestiOSSSL[3172:870700] CFNetwork SSLHandshake failed (-9824 -> -9829)
2016-06-13 08:22:37.793 TestiOSSSL[3172:870700] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9829)
2016-06-13 08:22:37.815 TestiOSSSL[3172:870685] Done : Error Domain=NSURLErrorDomain Code=-1206 "The server “ server.com” requires a client certificate." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x13de519b0>, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9829, NSUnderlyingError=0x13de4f280 {Error Domain=kCFErrorDomainCFNetwork Code=-1206 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=1, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x13de519b0>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9829, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9829, kCFStreamPropertySSLPeerCertificates=<CFArray 0x13dda0a70 [0x1a0dc2150]>{type = immutable, count = 2, values = (
    0 : <cert(0x13dda4970) s: Server.com i: Localhost CA>
    1 : <cert(0x13dda50d0) s: Localhost CA i: Localhost CA>
)}}}, NSErrorPeerCertificateChainKey=<CFArray 0x13dda0a70 [0x1a0dc2150]>{type = immutable, count = 2, values = (
    0 : <cert(0x13dda4970) s: Server.com i: Localhost CA>
    1 : <cert(0x13dda50d0) s: Localhost CA i: Localhost CA>
)}, NSLocalizedDescription=The server “server.com” requires a client certificate., NSErrorFailingURLKey=https://server.com/, NSErrorFailingURLStringKey=https://server.com/, NSErrorClientCertificateStateKey=1}

现在,如果我设置自己的 NSURLSession 并使用 URLSession:didReceiveChallenge:completionHandler: 回调:

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
theSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSURLSessionDataTask *task = [theSession dataTaskWithURL:[NSURL URLWithString:@"https://server.com"]
                                       completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                           NSLog(@"Done : %@", error ? error : @"OK");
                                       }];

接着:

- (void)URLSession:(NSURLSession *)session 
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
                             NSURLCredential *credential))completionHandler
{
    NSLog(@"Asking for credential");
    NSURLCredential *conf = [session.configuration.URLCredentialStorage defaultCredentialForProtectionSpace:challenge.protectionSpace];
    completionHandler(NSURLSessionAuthChallengeUseCredential, conf);
}

请注意我是如何使用 [session.configuration.URLCredentialStorage defaultCredentialForProtectionSpace:challenge.protectionSpace] 的,这是我认为 NSURLSession 的默认实现在收到身份验证质询时所做的。

这适用于这个特定的连接!这证明了凭证是可以的,并且在默认的 NSURLCredentialStorage 中正确注册为默认凭证。

但是围绕 didReceiveChallenge: 回调的任何解决方案都不好,因为我无法控制媒体播放器正在使用哪个 NSURLSession。

我已经尝试过CustomHTTPProtocol hack,但这也不起作用。

有什么建议吗?我已经浏览了所有类似的帖子,我找不到解决方案。这篇文章非常接近,但接受的答案对我来说没有意义,并且明显与 Apple 的文档相矛盾。

4

1 回答 1

0

尽管默认会话和 NSURLConnection 之间共享了许多功能,但显然不是。您是否尝试过调用该方法[NSURLSession sharedSession].configuration.URLCredentialStorage

另一种可能性是请求发生在一个单独的任务中,在这种情况下,可能无法以您尝试的方式进行,因为它将涉及不同的共享会话。如果是这种情况,您可能必须自己将凭证存储到钥匙串中,并相信其他进程将共享钥匙串并正确获取凭证。

于 2016-07-25T05:30:41.237 回答