2013-10-25 4 views
2

간단한 TCP 서버/클라이언트 설정이 있습니다. 이 연결은 실제로 잘 작동합니다.SSL/TLS를 지원하는 Objective-C 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 자습서 또는 클라이언트와 서버 측면 모두를 다루는 다른 자료가 있습니까?

+0

이 동일한 코드를 사용하고 연결의 양쪽 끝에? 그렇다면, 연결의 양쪽에서'kCFStreamSSLIsServer' 속성을'YES'로 설정하는 것처럼 보입니다. –

+0

나는 지금 사무실에 없다. 그러나 그것이 옳을 수도 있습니다. 'kCFStreamSSLIsServer'를 제거해야합니까? –

+0

나는이 코드 줄을 유지하고 싶다고 생각한다. 서버 연결의 측면에서 그대로 둔다. 그러나 클라이언트 측에서는이 값을 사전에 '아니오'로 설정하려고합니다. –

답변

1

이 코드에는 몇 가지 문제점이 있습니다. kCFStreamPropertySSLSettings를 사용하여 kCFStreamPropertyShouldCloseNativeSocket을 설정할 수 없으며 서버와 클라이언트 코드를 혼용해서는 안됩니다. 당신이 다음을 수행해야합니다 검증 체인 (See Apple doc on overriding chain validation)를 오버라이드 (override) 할 경우, 당신은 단지 클라이언트의 인증서

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

을 설정해야합니다 서버의

:

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)); 
+0

왜 'kCFStreamPropertyShouldCloseNativeSocket'을 설정해야하는지 설명해 주시겠습니까? –

+0

kCFStreamPropertyShouldCloseNativeSocket은 객체의 수명이 끝날 때 연결을 닫습니다. 그렇지 않으면 네이티브 소켓을 수동으로 닫아야합니다. – impact27

+0

설명해 주셔서 감사합니다. –