NSURLCredentialStorage setDefaultCredential: не работает для [NSURLSession sharedSession]
Я в замешательстве: моему приложению нужно подключиться к серверу, который использует самозаверяющие сертификаты для 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: завершение Handler: 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, но не могу найти решение для этого. Этот пост действительно близок, но принятый ответ не имеет смысла для меня и явно противоречит документации Apple.
1 ответ
Хотя между сеансом по умолчанию и NSURLConnection используется много функциональных возможностей, очевидно, что этот бит - нет. Вы пытались вызвать этот метод на [NSURLSession sharedSession].configuration.URLCredentialStorage
?
Другая возможность состоит в том, что запросы выполняются в отдельной задаче, и в этом случае может оказаться невозможным сделать это так, как вы пытаетесь, потому что это потребует другого общего сеанса. Если это так, вам, вероятно, придется самостоятельно сохранять учетные данные в цепочке для ключей и полагать, что другой процесс будет использовать цепочку для ключей и правильно извлекать учетные данные.