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 (вы действительно его запутали).

Проблема заключается в том, что по соображениям безопасности некоторые программы не любят самозаверяющие сертификаты. Это часто можно исправить, создав и установив свой собственный корневой сертификат и имея сертификат, подписанный вашим собственным корневым сертификатом.

Другие вопросы по тегам