Я пытаюсь подключить приложение 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 использует очень похожий стиль:
Как я уже упоминал ранее, я не эксперт в Objective-C (далеко от него), поэтому я могу ошибаться, но из того, что я видел, перемещение этих элементов в класс и их сохранение не казалось в любом случае.