2015-10-16 7 views
13

TCP/IPでTLSを使用してWindows C#サーバーにiOSアプリケーションを接続しようとしています。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ネゴシエーションの最初の段階が機能しているようです。私はNSStreamEventHasSpaceAvailableイベントにNSStreamデリゲートによって呼び出されるこの機能、持っているサーバ証明書を検証するために

// 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です。このケースではありません示唆この引用がありhttps://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html

:このアップルの開発者のリンクから

あなたのストリームデリゲートのイベントハンドラが に呼び出される時までに

があることを示しています使用可能なスペースがソケット上にある場合、システムは既にTLSチャネルを構築しており、接続のもう一方の端から証明書 チェーンを取得し、それを評価する信頼オブジェクト を作成しました。

これを修正する方法に関するヒント?

これにアクセスするにはどうすればいいですか?trustRef NSStreamのオブジェクトですか?

編集:返信100pholeため

感謝。このような

何か:この作業を取得しようとして

は、私は、これは問題とは何かを持っていると私はクラスにそれらのソケットに関連するすべてのアイテムを移動し、私の多くの試みの一つでかもしれないと思いました。

@interface Socket 
    CFReadStreamRef readStream; 
    CFWriteStreamRef writeStream; 

    NSInputStream *inputStream; 
    NSOutputStream *outputStream; 
@end 

しかし、かなりのコードの一般的なパターンのように見える私のGoogle検索に基づいて、そのためには、同じ結果:(

を思い付いた私は戻って上に示したバージョンに戻った。

そう、私は先に述べたように

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

あなたが参照しているWebページの注釈が表示されます。 '/ *入力ストリームと出力ストリームへの参照を保存しないように保存します。* /' – Wain

答えて

0

この質問に興味があるように思えるので、この問題が最終的にどのように解決されたかについての詳細を答えて質問を更新することにしました。

最初にいくつかの背景。私は以前の開発者からこのコードを継承しました。私の役割は、壊れたコードを動作させることでした。

アップルのiOSデベロッパーWebページの詳細を使用して接続コードを書き、書き直すのに多くの時間を費やしましたが、何も動作していないようです。

[self loadClientCertificates:certificates]; 

は一見のコードはOKだった:

は、私は最終的に私が継承され、間違って想定していたコードが働いていた、この機能で詳しく見ていくことにしました。この関数は、ファイルから証明書をロードするだけの機能を果たしました。しかし、より詳細な検査では、コードが証明書を正しく読み込んでいる間に、それらの証明書を呼び出し元に返送していませんでした。

証明書が正しく返されるようにコードを修正した後、接続コードが正常に機能し、SecTrustRefはNULLではなくなりました。要約すると

1)アップルのドキュメント、良い例を欠くことは正確であるように見えるん。何の証明書が前述のコーディングエラーのための接続APIを利用できるようにしないされているので、有効な証明書が接続交渉段階のために見つかりませんでしたので、SecTrustRef

2)理由は、NULLがあった、それはでした。あなたが同様のエラーが表示される場合

3)は、私の提案は、予想されるように記載されているように、式のiOSの側が動作するため、あなたのコードをチェックチェック、ダブルすることです。