我很难过:我的应用程序需要连接到使用 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 的文档相矛盾。