Подтвердить что ты не робот

IOS SecTrustRef Always NULL

Я пытаюсь подключить приложение iOS к серверу Windows С#, используя TLS через TCP/IP.

Соединение TLS использует ненадежные сертификаты, созданные из ненадежного корневого сертификата CA, используя утилиту makecert.

Чтобы протестировать эти сертификаты, я создал простой клиент С# и использовал эти сертификаты, которые он смог подключить и связался с сервером.

Я не умею разрабатывать iOS, но мне удалось найти код, который соединяет меня с сервером, следующим образом:

-(bool)CreateAndConnect:(NSString *) remoteHost withPort:(NSInteger) serverPort
{
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;

    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)(remoteHost),
                                       serverPort, &readStream, &writeStream);

    CFReadStreamSetProperty(readStream, kCFStreamPropertySocketSecurityLevel,
                            kCFStreamSocketSecurityLevelNegotiatedSSL);

    NSInputStream *inputStream = (__bridge_transfer NSInputStream *)readStream;
    NSOutputStream *outputStream = (__bridge_transfer NSOutputStream *)writeStream;

    [inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];

    // load certificate from servers exported p12 file
    NSArray *certificates = [[NSArray alloc] init];
    [self loadClientCertificates:certificates];

    NSDictionary *sslSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                 (id)kCFBooleanFalse, (id)kCFStreamSSLValidatesCertificateChain,
                                 certificates,(id)kCFStreamSSLCertificates,
                                 nil];

    [inputStream setProperty:sslSettings forKey:(__bridge NSString *)kCFStreamPropertySSLSettings];

    [inputStream setDelegate:self];
    [outputStream setDelegate:self];

    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

    CFReadStreamOpen(readStream);
    CFWriteStreamOpen(writeStream);

    return true;
}

Код также, похоже, выполняет некоторую форму согласования TLS, поскольку сервер С# отклоняет соединение, если сертификаты p12 не предоставляются как часть настроек NSStream.

Итак, похоже, что работает первый этап согласования TLS.

Чтобы проверить сертификат сервера, у меня есть эта функция, которая вызывается делегатом NSStream в событии NSStreamEventHasSpaceAvailable:

// return YES if certificate verification is successful, otherwise NO
-(BOOL) VerifyCertificate:(NSStream *)stream
{
    NSData *trustedCertData = nil;
    BOOL result             = NO;
    SecTrustRef trustRef    = NULL;
    NSString *root_certificate_name      = @"reference_cert";
    NSString *root_certificate_extension = @"der";

    /* Load reference cetificate */
    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    trustedCertData = [NSData dataWithContentsOfFile:[bundle pathForResource: root_certificate_name ofType: root_certificate_extension]];

    /* get trust object */
    /* !!!!! error is here as trustRef is NULL !!!! */
    trustRef = (__bridge SecTrustRef)[stream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust];

    /* loacate the reference certificate */
    NSInteger numCerts = SecTrustGetCertificateCount(trustRef);
    for (NSInteger i = 0; i < numCerts; i++) {
        SecCertificateRef secCertRef = SecTrustGetCertificateAtIndex(trustRef, i);
        NSData *certData = CFBridgingRelease(SecCertificateCopyData(secCertRef));
        if ([trustedCertData isEqualToData: certData]) {
            result = YES;
            break;
        }
    }
    return result;
}

Теперь проблема заключается в том, что независимо от того, что я пытаюсь, объект trustRef всегда имеет значение null.

Из этой ссылки разработчика Apple: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html

Есть такая цитата, которая предполагает, что этого не должно быть:

К тому времени, когда потоковый делегат обработчика событий вызван указывают, что в гнезде есть место, а система уже построила канал TLS, получила сертификат цепочку с другого конца соединения и создал объект доверия оценить его.

Любые подсказки о том, как это исправить?

Как я могу получить доступ к этому объекту trustRef для NSStream?

Edit:

Спасибо за ответ 100phole.

В попытке заставить это работать, я подумал, что это может иметь какое-то отношение к проблеме, и в одной из моих многочисленных попыток я переместил все эти связанные сокет элементы в класс:

Что-то вроде этого:

@interface Socket
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;

    NSInputStream *inputStream;
    NSOutputStream *outputStream;
@end

Но это привело к тем же результатам: (

Я вернусь обратно к версии, показанной выше, потому что на основе моего поиска Google это выглядит довольно условно.

Например, даже этот код с сайта Apple Developer использует очень похожий стиль:

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Streams/Articles/NetworkStreams.html#//apple_ref/doc/uid/20002277-BCIDFCDI

Как я уже упоминал ранее, я не эксперт в Objective-C (далеко от него), поэтому я могу ошибаться, но из того, что я видел, перемещение этих элементов в класс и их сохранение не казалось в любом случае.

4b9b3361

Ответ 1

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

Сначала немного фона. Я унаследовал этот код от предыдущего разработчика, и моя роль состояла в том, чтобы заставить сломанный код работать.

Я потратил много времени на запись и переписывание кода подключения с помощью деталей с веб-страницы разработчика Apple iOS, но ничего не работало.

Наконец, я решил более внимательно изучить эту функцию, код, который я унаследовал и неправильно принял, работал:

[self loadClientCertificates:certificates];

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

После исправления этого кода, чтобы он правильно вернул сертификаты, код подключения работал нормально, а SecTrustRef больше не был NULL.

Вкратце:

1) Документация Apple, несмотря на отсутствие хороших примеров, кажется точным.

2). Причина, по которой SecTrustRef была NULL, заключалась в том, что для фазы переговоров о подключении не было найдено действительного сертификата, и это было вызвано тем, что сертификаты, предоставляемые API соединения из-за ранее упомянутых ошибка кодирования.

3) Если вы видите подобную ошибку, мое предложение состояло в том, чтобы проверить и дважды проверить ваш код, потому что, как и следовало ожидать, сторона iOS уравнения работает как документально.