iOS 9 ATS блокирует HTTPS-запрос к серверу с помощью самоподписанного сертификата
Эта проблема полностью устранена из-за переполнения стека, и я провел последние 2 дня, пробуя бесчисленные комбинации конфигураций ATP и заставляя мое приложение работать. Я собираюсь быть внимательным с моей проблемой, поскольку кажется, что крошечная вещь может повлиять на то, как решить эту проблему.
Я только недавно настроил сервер Ubuntu 14 с включенными SSL и TLS 1.2. На моем сервере находятся мои серверные скрипты, от которых зависит мое приложение. В моем приложении я использую NSMutableURLRequest для запроса моего API с сервера следующим образом:
NSString * noteDataString = [NSString stringWithFormat:@"email=%@&password=%@&type=%@", companyEmail, companyPassword, @"login"];
NSData * postData = [noteDataString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString * postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[postData length]];
NSMutableURLRequest * request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:@"https://mydomain.me:443/path/to/file.php"]];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
NSHTTPURLResponse * urlResponse = nil;
NSError * error = nil;
NSData * responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error];
NSString * result = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(@"Response Code: %ld", (long)[urlResponse statusCode]);
Когда я копирую URL в Chrome, отображается правильное возвращение PHP. При запросе из Xcode 7 на iOS 9 я получаю эту ошибку:
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
Я использовал бесчисленные настройки info.plist, как видно из аналогичных проблем. Я попытался отключить потребность в прямой секретности, я попытался включить произвольную загрузку, я попытался использовать домен исключения mydomain.me и его субдомены. Единственный прогресс, которого я достигаю, это код ошибки -9813 при переключении на -9802.
Мне известно о добавлении методов делегата для NSURLRequest, но они никогда не вызываются и не считаются избыточными для решения этой проблемы.
Я построил большую часть приложения на сервере MAMP, используя localhost и http, и запросы работали, когда я тогда включал произвольные загрузки, так что с моим plist все в порядке.
Это ошеломляет, почему у меня есть особый случай, и я знал, что переполнение стека было местом для таких ситуаций!
Спасибо, и я надеюсь, что это поможет гораздо большему количеству разработчиков, чем я.
2 ответа
Решение. Спасибо всем в комментариях за указание мне в правильном направлении. Решение состояло в том, чтобы создать объект NSObject, который обрабатывал NSURLRequests для этого особого случая.
Кредит: https://www.cocoanetics.com/2010/12/nsurlconnection-with-self-signed-certificates/
Ниже приведена почти прямая копия учебника по ссылке, но я подумал, что было бы легче остаться здесь.
Так,
Мне пришлось создать новый класс NSObject со следующим кодом:
BWWebService.h
#import <Foundation/Foundation.h>
@interface BWWebService : NSObject{
NSMutableData *receivedData;
NSURLConnection *connection;
NSStringEncoding encoding;
}
- (id)initWithURL:(NSURL *)url;
@end
BWWebService.m
#import "BWWebService.h"
@implementation BWWebService
- (id)initWithURL:(NSURL *)url{
if (self = [super init]){
NSURLRequest * request = [NSURLRequest requestWithURL:url];
connection = [NSURLConnection connectionWithRequest:request delegate:self];
[connection start];
}
return self;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
// Every response could mean a redirect
receivedData = nil;
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)
[response textEncodingName]);
encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
if (!receivedData){
// No store yet, make one
receivedData = [[NSMutableData alloc] initWithData:data];
}else{
// Append to previous chunks
[receivedData appendData:data];
}
}
// All worked
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSString * xml = [[NSString alloc] initWithData:receivedData encoding:encoding];
NSLog(@"%@", xml);
}
// And error occurred
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(@"Error retrieving data, %@", [error localizedDescription]);
}
// To deal with self-signed certificates
- (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]){
// we only trust our own domain
if ([challenge.protectionSpace.host isEqualToString:@"myuntrusteddomain.me"]){
NSURLCredential * credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
@end
И если этого было недостаточно, чтобы сделать запрос, я использовал следующее вместо моего первоначального запроса:
NSURL * url = [NSURL URLWithString:@"https://myuntrusteddomain.me:443/path/to/script.php"];
BWWebService * webService;
webService = [[BWWebService alloc] initWithURL:url];
Я знаю, что это не POST данные, как оригинал, но это будет позже. Я уверен, что это будет вопрос обработки POST в initWithURL.
Спасибо всем.
Изменить: Похоже, что это приложение решения работает только с Разрешить произвольные нагрузки, установленным на ДА.
Откройте XCode, Command-Shift-2, введите 9813, и вы сразу же найдете -9813 = errSSLNoRootCert, чего и следовало ожидать, так как у вашего самозаверяющего сертификата нет корневого сертификата, а -9802 = errSSLFatalAlert (вы действительно его запутали).
Проблема заключается в том, что по соображениям безопасности некоторые программы не любят самозаверяющие сертификаты. Это часто можно исправить, создав и установив свой собственный корневой сертификат и имея сертификат, подписанный вашим собственным корневым сертификатом.