Как использовать NSURLConnection для подключения с SSL для ненадежного сертификата?
у меня есть следующий простой код для подключения к веб-странице SSL
NSMutableURLRequest *urlRequest=[NSMutableURLRequest requestWithURL:url];
[ NSURLConnection sendSynchronousRequest: urlRequest returningResponse: nil error: &error ];
за исключением того, что он дает ошибку, если сертификат является самозаверяющим Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".
есть ли способ установить его для приема соединений в любом случае (так же, как в браузере вы можете нажать accept) или способ обойти его?
13 ответов:
существует поддерживаемый API для выполнения этого! Добавить что-то вроде этого в свой
NSURLConnection
делегат:- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; } - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) if ([trustedHosts containsObject:challenge.protectionSpace.host]) [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; }
отметим, что
connection:didReceiveAuthenticationChallenge:
может отправить свое сообщение, чтобы бросить вызов.отправитель (намного) позже, после представления диалогового окна пользователю при необходимости и т. д.
Если вы не хотите (или не можете) использовать частные API, есть библиотека с открытым исходным кодом (лицензия BSD) под названием ASIHTTPRequest это обеспечивает обертку вокруг нижнего уровня
CFNetwork APIs
. Они недавно ввели возможность разрешитьHTTPS connections
использование самозаверяющих или ненадежных сертификатов с помощью-setValidatesSecureCertificate:
API. Если вы не хотите использовать всю библиотеку, вы можете использовать источник в качестве ссылки для реализации той же функциональности самостоятельно.
В идеале должно быть только два сценария, когда приложение iOS должно будет принять ненадежный сертификат.
сценарий A: вы подключены к тестовой среде, которая использует самозаверяющий сертификат.
сценарий B: вы Проксируете
HTTPS
трафик, используяMITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.
Прокси вернет сертификат, подписанный собственной подписью центра сертификации, поэтому, что прокси-сервер способен улавливатьHTTPS
трафик.производственные хосты никогда не должны использовать ненадежные сертификаты для очевидным причинам.
Если вам нужно, чтобы симулятор iOS принял ненадежный сертификат для целей тестирования, настоятельно рекомендуется не изменять логику приложения, чтобы отключить встроенную проверку сертификатов, предоставляемую
NSURLConnection
API-интерфейсы. Если приложение будет выпущено на публику без удаления этой логики, оно будет восприимчиво к атакам "человек в середине".рекомендуемый способ принять недоверенные сертификаты для целей тестирования-это импорт сертификата Центра сертификации(ЦС), который подписал сертификат на ваш симулятор iOS или устройство iOS. Я написал быстрый пост в блоге, который демонстрирует, как это сделать, что симулятор iOS по адресу:
NSURLRequest
имеет частный метод под названиемsetAllowsAnyHTTPSCertificate:forHost:
, который будет делать именно то, что вы хотите. Вы могли бы определитьallowsAnyHTTPSCertificateForHost:
метод onNSURLRequest
через категорию, и установите его для возвратаYES
для узла, который вы хотите переопределить.
чтобы дополнить принятый ответ, для гораздо лучшей безопасности вы можете добавить свой сертификат сервера или свой собственный сертификат корневого ЦС в keychain( https://stackoverflow.com/a/9941559/1432048), однако выполнение этого в одиночку не заставит NSURLConnection автоматически аутентифицировать ваш самозаверяющий сервер. Вам все равно нужно добавить приведенный ниже код в делегат NSURLConnection, он скопирован из примера кода Apple AdvancedURLConnections и нужно добавить две файлы(учетные данные.h, верительные грамоты.m) от примера кода apple к вашим проектам.
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; } - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { // if ([trustedHosts containsObject:challenge.protectionSpace.host]) OSStatus err; NSURLProtectionSpace * protectionSpace; SecTrustRef trust; SecTrustResultType trustResult; BOOL trusted; protectionSpace = [challenge protectionSpace]; assert(protectionSpace != nil); trust = [protectionSpace serverTrust]; assert(trust != NULL); err = SecTrustEvaluate(trust, &trustResult); trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified)); // If that fails, apply our certificates as anchors and see if that helps. // // It's perfectly acceptable to apply all of our certificates to the SecTrust // object, and let the SecTrust object sort out the mess. Of course, this assumes // that the user trusts all certificates equally in all situations, which is implicit // in our user interface; you could provide a more sophisticated user interface // to allow the user to trust certain certificates for certain sites and so on). if ( ! trusted ) { err = SecTrustSetAnchorCertificates(trust, (CFArrayRef) [Credentials sharedCredentials].certificates); if (err == noErr) { err = SecTrustEvaluate(trust, &trustResult); } trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified)); } if(trusted) [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; } [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; }
Я не могу взять на себя ответственность за это, но этот я нашел работал очень хорошо для моих потребностей.
shouldAllowSelfSignedCert
мойBOOL
переменной. Просто добавьте к вашемуNSURLConnection
делегат, и вы должны быть rockin для быстрого обхода на основе каждого соединения.- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)space { if([[space authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) { if(shouldAllowSelfSignedCert) { return YES; // Self-signed cert will be accepted } else { return NO; // Self-signed cert will be rejected } // Note: it doesn't seem to matter what you return for a proper SSL cert // only self-signed certs } // If no other authentication is required, return NO for everything else // Otherwise maybe YES for NSURLAuthenticationMethodDefault and etc. return NO; }
В iOS 9 SSL-соединения не будут работать для всех недействительных или самозаверяющих сертификатов. Это поведение по умолчанию для нового Безопасность Транспорта Приложения функция в iOS 9.0 или более поздней версии, а также в OS X 10.11 и более поздних версиях.
вы можете переопределить это поведение в
Info.plist
, установивNSAllowsArbitraryLoads
toYES
наNSAppTransportSecurity
словарь. тем не менее, я рекомендую переопределить этот параметр для целей тестирования только.дополнительные сведения см. В разделе приложение Транспорт комментарием здесь.
обходной путь категории, опубликованный Натаном де Фрисом, пройдет проверку частного API AppStore и будет полезен в тех случаях, когда у вас нет контроля над
вы должны использовать
NSURLConnectionDelegate
чтобы разрешить HTTPS-соединения, и есть новые обратные вызовы с iOS8.не рекомендуется:
connection:canAuthenticateAgainstProtectionSpace: connection:didCancelAuthenticationChallenge: connection:didReceiveAuthenticationChallenge:
вместо этого следует объявить:
connectionShouldUseCredentialStorage:
- отправлено, чтобы определить, должен ли загрузчик URL использовать хранилище учетных данных для проверки подлинности соединения.
connection:willSendRequestForAuthenticationChallenge:
- сообщает делегату, что соединение отправит запрос на аутентификацию вызов.С
willSendRequestForAuthenticationChallenge
можно использоватьchallenge
как вы сделали с устаревшими методами, например:// Trusting and not trusting connection to host: Self-signed certificate [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
Я опубликовал некоторый код gist (основанный на чужой работе, которую я отмечаю), который позволяет вам правильно аутентифицироваться против самогенерируемого сертификата (и как получить бесплатный сертификат - см. комментарии внизу Cocoanetics)
мой код здесь github
Если вы хотите продолжать использовать sendSynchronousRequest Я работаю в этом решении:
FailCertificateDelegate *fcd=[[FailCertificateDelegate alloc] init]; NSURLConnection *c=[[NSURLConnection alloc] initWithRequest:request delegate:fcd startImmediately:NO]; [c setDelegateQueue:[[NSOperationQueue alloc] init]]; [c start]; NSData *d=[fcd getData];
посмотреть можно здесь: цель-C SSL Синхронное соединение
С AFNetworking я успешно использовал https webservice с кодом ниже,
NSString *aStrServerUrl = WS_URL; // Initialize AFHTTPRequestOperationManager... AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; manager.requestSerializer = [AFJSONRequestSerializer serializer]; manager.responseSerializer = [AFJSONResponseSerializer serializer]; [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; manager.securityPolicy.allowInvalidCertificates = YES; [manager POST:aStrServerUrl parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { successBlock(operation, responseObject); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { errorBlock(operation, error); }];
вы можете использовать этот код
-(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodServerTrust) { [[challenge sender] useCredential:[NSURLCredential credentialForTrust:[[challenge protectionSpace] serverTrust]] forAuthenticationChallenge:challenge]; } }
использовать
-connection:willSendRequestForAuthenticationChallenge:
вместо этих устаревших методовне рекомендуется:
-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace -(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge -(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge