NSURLCredentialStorage setDefaultCredential:不适用于[NSURLSession sharedSession]

时间:2016-06-13 06:46:27

标签: ios nsurlsession nsurlcredential nsurlcredentialstorage

我很难过:我的应用需要连接到使用自签名证书进行HTTPS的服务器,并且需要客户端身份验证。更糟糕的是,我实际上需要iOS媒体播放器才能连接到该服务器,所以我已经关注Apple的instruction了这封信:

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:callback:

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黑客攻击,但这也无法解决。

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

1 个答案:

答案 0 :(得分:0)

虽然默认会话和NSURLConnection之间共享了很多功能,但显然不是这样。您是否尝试在Unit: microseconds expr min lq mean median uq max neval mutate(var1 = nth(na.omit(var1), -1L)) 795.270 830.4880 1022.196 897.6375 1026.795 4437.483 1000 mutate(var1 = tail(na.omit(var1))) 791.035 825.6165 1011.288 892.6270 1037.463 3406.842 1000 mutate(var1 = aa(var1)) 788.085 825.5180 1108.872 888.9945 1036.664 102915.926 1000 上调用该方法?

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