我試圖使用TLS通過TCP/IP將iOS應用程序連接到Windows C#服務器。iOS SecTrustRef始終爲NULL
的TLS連接,使用從使用makecert效用的不可信CA根認證證書創建不受信任的證書。
爲了測試這些證書,我創建了一個簡單的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協商,因爲如果未將p12證書作爲NSStream設置的一部分提供,C#服務器會拒絕連接。
所以看起來像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對象始終爲空。
有這句話暗示這不應該是這樣的:
通過您的流委託的事件處理程序被調用到 時表示有系統已經構建了TLS通道,從連接的另一端獲得證書 鏈,並創建了一個信任對象 來評估它。
有關如何解決此問題的任何提示?
如何才能訪問該NSStream的對象trustRef?
編輯:
感謝您的答覆100phole。
在試圖得到這個工作,我認爲這可能有一些做的問題,在我的許多嘗試之一,我感動了所有的插座相關的項目成爲階級:
事情是這樣的:
@interface Socket
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
NSInputStream *inputStream;
NSOutputStream *outputStream;
@end
但是,想出了相同的結果:(
我只恢復到上面顯示的版本,因爲,根據我的谷歌搜索,這似乎是一個相當常見的代碼模式。
例如,從蘋果開發者網站甚至這個代碼使用了風格極爲相似:
正如我前面提到的,我在Objective-C沒有專家(遠非如此),所以我可能是錯的,但從我所見到的情況來看,將這些項目移到一個班級並讓他們堅持下去似乎沒有什麼區別。
您的流似乎是方法中的局部變量,這意味着它們將在方法返回時被銷燬。 – l00phole
只看你的代碼足夠長的時間來問...爲什麼不使用內建的NSURL會話和連接類?連接設置和身份驗證握手都是爲您處理的(必要時使用鉤子進行插入)。 –
你可以在你參考的網頁上看到這樣一條註釋:/ *存儲對輸入和輸出流的引用,以便它們不會消失.... * /' – Wain