3

シンプルな TCP サーバー/クライアントのセットアップがあります。この接続は実際にうまく機能します。

ここで、ソケット接続に SSL / TLS 暗号化を実装したいと考えました。キーチェーン アクセスを使用して PKCS12 証明書を作成しました。私のサーバーでは、受け入れコールバック内に次のコードがあります。

NSString *certificatePath = [[NSBundle mainBundle] pathForResource:@"TCPServerCertificate" ofType:@"p12"];
NSData *certificateData = [NSData dataWithContentsOfFile:certificatePath];

CFArrayRef keyRef;
OSStatus status = SecPKCS12Import((__bridge CFDataRef)certificateData, (__bridge CFDictionaryRef)@{(__bridge NSString *)kSecImportExportPassphrase: @"1234"}, &keyRef);

if (status != noErr) {
    NSLog(@"PKCS12 import error %i", status);
    return;
}

CFDictionaryRef identityDict = CFArrayGetValueAtIndex(keyRef, 0);
SecIdentityRef identityRef = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);

SecCertificateRef certificate;
status = SecIdentityCopyCertificate(identityRef, &certificate);

if (status != noErr) {
    NSLog(@"sec identity copy failed: %i", status);
    return;
}

NSArray *certificates = [NSArray arrayWithObjects:(__bridge id)identityRef, (__bridge id)certificate, nil];

NSDictionary *settings = @{(NSString *)kCFStreamPropertyShouldCloseNativeSocket:    [NSNumber numberWithBool:YES],
                           (NSString *)kCFStreamSSLValidatesCertificateChain:       [NSNumber numberWithBool:YES],
                           (NSString *)kCFStreamSSLAllowsExpiredCertificates:       [NSNumber numberWithBool:NO],
                           (NSString *)kCFStreamSSLAllowsExpiredRoots:              [NSNumber numberWithBool:NO],
                           (NSString *)kCFStreamSSLAllowsAnyRoot:                   [NSNumber numberWithBool:YES],
                           (NSString *)kCFStreamSSLCertificates:                    certificates,
                           (NSString *)kCFStreamSSLIsServer:                        [NSNumber numberWithBool:YES],
                           (NSString *)kCFStreamSSLLevel:                           (NSString *)kCFStreamSocketSecurityLevelTLSv1};

CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);

次にNSStream、ストリームのインスタンスを作成し、それらを別のクラスで処理します。

サーバーを実行してクライアントに接続するとNSStreamEventOpenCompleted、デリゲートで定期的に取得されます。接続を閉じるだけでストリームまたはイベントに書き込もうとすると、次のエラーが発生します。

2013-10-25 13:27:08.584 TCPServer[6435:303] CFNetwork SSLHandshake failed (-9800)
2013-10-25 13:27:08.584 TCPServer[6435:303] NSStreamEventOpenCompleted
2013-10-25 13:27:08.585 TCPServer[6435:303] NSStreamEventErrorOccurred

クライアント側で何を実装しなければならないのだろうか。また、データを送信したり、クライアント側から切断したりするときにハンドシェイクが失敗するのはなぜだろうか。このエラーが発生するたびに、クライアントはまだ接続されていると見なします。

クライアント側とサーバー側の両方をカバーする優れた TCP SSL/TLS チュートリアルやその他の資料はありますか?

4

2 に答える 2

1

このコードにはいくつかの問題があります: kCFStreamPropertyShouldCloseNativeSocket を kCFStreamPropertySSLSettings で設定することはできず、サーバー コードとクライアント コードを混在させないでください。

サーバーの場合は、証明書を設定するだけです

CFReadStreamSetProperty(read, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(write, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
//kCFStreamPropertySocketSecurityLevel
//Note: If you set this key, you must do so before setting any other SSL options, such as kCFStreamPropertySSLSettings.
CFReadStreamSetProperty(read, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
CFWriteStreamSetProperty(write, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
//Creating server dictionnary

//kCFStreamSSLIsServer
//If the value of this key is kCFBooleanTrue, the kCFStreamSSLCertificates key must contain a valid value

//kCFStreamSSLCertificates
//Security property key whose value is a CFArray of SecCertificateRefs except for the first element in the array, which is a SecIdentityRef.
//For more information, see SSLSetCertificate() in Security/SecureTransport.h.

NSDictionary *settings = @{(id)kCFStreamSSLCertificates:                    certificates,
                           (id)kCFStreamSSLIsServer:                        @YES};


//Apply settings
CFReadStreamSetProperty(read, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)(settings));
CFWriteStreamSetProperty(write, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)(settings));

クライアントの場合、検証チェーンをオーバーライドする場合 (チェーン検証のオーバーライドに関する Apple のドキュメントを参照)、次の手順を実行する必要があります。

CFReadStreamSetProperty(read, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(write, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
//kCFStreamPropertySocketSecurityLevel
//Note: If you set this key, you must do so before setting any other SSL options, such as kCFStreamPropertySSLSettings.

CFReadStreamSetProperty(read, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
CFWriteStreamSetProperty(write, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);

//DO NOT USE kCFStreamPropertySSLContext as it overrides the following configuration
//create dictionnary for kCFStreamPropertySSLSettings

//keys for dictionnary we want to change:kCFStreamSSLAllowsExpiredCertificates;kCFStreamSSLAllowsExpiredRoots;kCFStreamSSLAllowsAnyRoot;
//kCFStreamSSLValidatesCertificateChain => no need to worry about the root
//kCFStreamSSLPeerName kCFNull prevents name verification


settings = @{(id)kCFStreamSSLValidatesCertificateChain: @NO,//The delegate will verify this
             (id)kCFStreamSSLPeerName: (id)kCFNull};//prevents name verification if server is not fixed (eg. IP)

//Apply settings
CFReadStreamSetProperty(read, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)(settings));
CFWriteStreamSetProperty(write, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)(settings));
于 2014-06-15T21:01:42.457 に答える