Как использовать NSURLConnection для соединения с SSL для ненадежного сертификата?
У меня есть следующий простой код для подключения к веб-странице SSL
NSMutableURLRequest *urlRequest=[NSMutableURLRequest requestWithURL:url];
[ NSURLConnection sendSynchronousRequest: urlRequest returningResponse: nil error: &error ];
За исключением того, что он дает ошибку, если cert является самоподписанным Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".
Есть ли способ установить, что он принимает соединения в любом случае (как в браузере, который вы можете нажать "принять" ), или способ обойти его?
Ответы
Ответ 1
Для этого есть поддерживаемый 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:
может отправить свое сообщение на вызов .sender(много) позже, после предоставления пользователю диалогового окна, если это необходимо, и т.д.
Ответ 2
Если вы не хотите (или не можете) использовать частные API, то есть библиотека с открытым исходным кодом (BSD), называемая ASIHTTPRequest который обеспечивает обертку вокруг нижнего уровня CFNetwork APIs
. Недавно они представили возможность разрешать HTTPS connections
использование самоподписанных или ненадежных сертификатов с API -setValidatesSecureCertificate:
. Если вы не хотите втягивать всю библиотеку, вы можете использовать источник как ссылку для реализации той же самой функции самостоятельно.
Ответ 3
В идеале должны быть только два сценария, когда приложение iOS должно принять недопустимый сертификат.
Сценарий A: вы подключены к тестовой среде, в которой используется самозаверяющий сертификат.
Сценарий B: вы трассируете трафик HTTPS
с помощью MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.
. Proxies вернет сертификат, подписанный самозаверяющим ЦС, чтобы прокси-сервер смог захватить трафик HTTPS
.
Производительные хосты никогда не должны использовать недостоверные сертификаты для очевидных причин.
Если вам нужно, чтобы iOS-симулятор принял недостоверный сертификат для целей тестирования, настоятельно рекомендуется не изменять логику приложения, чтобы отключить встроенную проверку сертификатов, предоставляемую API-интерфейсом NSURLConnection
. Если приложение будет выпущено публике, не удаляя эту логику, оно будет восприимчиво к атакам "человек в середине".
Рекомендуемым способом принятия сертификатов без проверки для целей тестирования является импорт сертификата центра сертификации (CA), который подписывал сертификат на вашем iOS-симуляторе или устройстве iOS. Я написал быстрый пост в блоге, в котором демонстрируется, как это сделать, что симулятор iOS:
принятие ненадежных сертификатов с помощью симулятора ios
Ответ 4
NSURLRequest
имеет частный метод под названием setAllowsAnyHTTPSCertificate:forHost:
, который будет делать именно то, что вы хотите. Вы можете определить метод allowsAnyHTTPSCertificateForHost:
на NSURLRequest
через категорию и установить его для возврата YES
для хоста, который вы хотите переопределить.
Ответ 5
Я не могу поверить в это, но этот, который я нашел, работал очень хорошо для моих нужд. shouldAllowSelfSignedCert
- моя переменная BOOL
. Просто добавьте к вашему делегату NSURLConnection
, и вы должны быть быстрым для быстрого обхода на основе каждого соединения.
- (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;
}
Ответ 6
Чтобы дополнить принятый ответ, для обеспечения большей безопасности вы можете добавить свой сертификат сервера или собственный корневой сертификат ЦС в цепочку ключей (fooobar.com/questions/24877/...), однако это само по себе не заставит NSURLConnection автоматически аутентифицировать ваш самозаверяющий сервер. Вам все равно нужно добавить приведенный ниже код к делегату NSURLConnection, он скопирован из примера кода Apple AdvancedURLConnections, и вам нужно добавить два файла (Credentials.h, Credentials.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 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];
}
Ответ 7
В iOS 9, SSL-соединения будут терпеть неудачу для всех недействительных или самозаверяющих сертификатов. Это поведение по умолчанию новой функции App Transport Security в iOS 9.0 или новее, а также в OS X 10.11 и новее.
Вы можете переопределить это поведение в Info.plist
, установив NSAllowsArbitraryLoads
в YES
в словаре NSAppTransportSecurity
. Однако я рекомендую переопределить этот параметр только для целей тестирования.
![введите описание изображения здесь]()
Информацию см. в App Transport Technote здесь.
Ответ 8
Обходное решение категории, опубликованное Nathan de Vries, будет проходить проверку Private API AppStore и полезно в тех случаях, когда вы не контролируете объект NSUrlConnection
.
Одним из примеров является NSXMLParser
, который откроет указанный вами URL-адрес, но не выставляет NSURLRequest
или NSUrlConnection
.
В iOS 4 обходной путь все еще работает, но только на устройстве, Simulator больше не вызывает метод allowsAnyHTTPSCertificateForHost:
.
Ответ 9
Вы должны использовать 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];
Ответ 10
Я отправил некоторый код gist (основанный на чьей-то другой работе, которую я отмечаю), которая позволяет вам правильно аутентифицироваться против самогенерируемого сертификата (и как получить бесплатный сертификат - см. нижнюю часть комментариев Cocoanetics)
Мой код здесь github
Ответ 11
Если вы хотите использовать 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];
вы можете увидеть его здесь: Objective-C Синхронное подключение SSL
Ответ 12
С 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);
}];
Ответ 13
Вы можете использовать этот код
-(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