2015-10-16 5 views
13

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

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

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

Я не квалифицирован в развитии 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 переговоров, поскольку сервер C# отклоняет соединение, если сертификаты 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, использует очень похожий стиль:

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

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

+2

Ваши потоки являются локальными переменными в методе, что означает, что они будут уничтожены при возврате метода. – l00phole

+0

Посмотрел ваш код достаточно долго, чтобы спросить ...почему бы не использовать встроенные классы NSURL и классы соединений? Настройка соединения и авторизационные рукопожатия обрабатываются для вас (при необходимости перехватывать крючки). –

+0

Вы видите примечание на веб-странице, на которое вы ссылаетесь: '/ * Храните ссылку на входные и выходные потоки, чтобы они не исчезали .... * /' – Wain

ответ

0

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

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

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

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

[self loadClientCertificates:certificates]; 

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

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

В итоге:

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

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

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