2015-10-16 67 views
13

我試圖使用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對象始終爲空。

從這個蘋果開發者鏈接:https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html

有這句話暗示這不應該是這樣的:

通過您的流委託的事件處理程序被調用到 時表示有系統已經構建了TLS通道,從連接的另一端獲得證書 鏈,並創建了一個信任對象 來評估它。

有關如何解決此問題的任何提示?

如何才能訪問該NSStream的對象trustRef

編輯:

感謝您的答覆100phole。

在試圖得到這個工作,我認爲這可能有一些做的問題,在我的許多嘗試之一,我感動了所有的插座相關的項目成爲階級:

事情是這樣的:

@interface Socket 
    CFReadStreamRef readStream; 
    CFWriteStreamRef writeStream; 

    NSInputStream *inputStream; 
    NSOutputStream *outputStream; 
@end 

但是,想出了相同的結果:(

我只恢復到上面顯示的版本,因爲,根據我的谷歌搜索,這似乎是一個相當常見的代碼模式。

例如,從蘋果開發者網站甚至這個代碼使用了風格極爲相似:

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]; 

乍一看代碼看起來OK。該函數只是從文件加載證書。但仔細觀察,當代碼正確加載證書時,它並沒有將這些證書返回給調用者!

修復該代碼以便正確返回證書後,連接代碼工作正常,並且SecTrustRef不再爲NULL。

總結:

1)蘋果的文檔,同時缺乏很好的例子確實出現了準確的。

2)的原因SecTrustRef是NULL是因爲沒有有效的證書,可以用於連接談判階段發現,那是因爲沒有證書,其中被提供給連接API由於前面提到的編碼錯誤。

3)如果您看到一個類似的錯誤,我的建議是檢查,並仔細檢查你的代碼,因爲正如所預料的,方程的iOS的側面可以作爲證明。

相關問題