0

我在爲iOS編寫一個客戶端,通過SSL/TLS連接到我的服務器。我決定使用NSStream實現。現在我的項目由於SSL握手而停止了,我不知道如何處理它,並且找不到代理人的任何示例。據我所知,證書傳遞應該在NSStreamEventHasSpaceAvailable中,但實際上我不明白。我需要通過我的證書(他沒有安裝在設備上)。有人能幫我嗎?NSStream與我的證書進行SSL握手

此代碼崩潰與此:

2015-02-09 18:58:28.902 Test CFNetwork SSLHandshake failed (-9807) 
2015-02-09 18:58:28.903 Test unexpected NSStreamEventErrorOccurred: Error Domain=NSOSStatusErrorDomain Code=-9807 "The operation couldn’t be completed. (OSStatus error -9807.)" 
2015-02-09 18:58:28.903 Test unexpected NSStreamEventErrorOccurred: Error Domain=NSOSStatusErrorDomain Code=-9807 "The operation couldn’t be completed. (OSStatus error -9807.)" 

我有什麼:

@interface NSStream (FSNetworkAdditions) 

+ (void)qNetworkAdditions_getStreamsToHostNamed:(NSString *)hostName 
              port:(NSInteger)port 
            inputStream:(out NSInputStream **)inputStreamPtr 
            outputStream:(out NSOutputStream **)outputStreamPtr; 

@end 

@implementation NSStream (FSNetworkAdditions) 

+ (void)qNetworkAdditions_getStreamsToHostNamed:(NSString *)hostName 
              port:(NSInteger)port 
            inputStream:(out NSInputStream **)inputStreamPtr 
            outputStream:(out NSOutputStream **)outputStreamPtr 
{ 
    CFReadStreamRef  readStream; 
    CFWriteStreamRef writeStream; 

    assert(hostName != nil); 
    assert((port > 0) && (port < 65536)); 
    assert((inputStreamPtr != NULL) || (outputStreamPtr != NULL)); 

    readStream = NULL; 
    writeStream = NULL; 

    CFStreamCreatePairWithSocketToHost(
             NULL, 
             (__bridge CFStringRef) hostName, 
             port, 
             ((inputStreamPtr != NULL) ? &readStream : NULL), 
             ((outputStreamPtr != NULL) ? &writeStream : NULL) 
             ); 


    if (inputStreamPtr != NULL) { 
     *inputStreamPtr = CFBridgingRelease(readStream); 
    } 
    if (outputStreamPtr != NULL) { 
     *outputStreamPtr = CFBridgingRelease(writeStream); 
    } 


} 

- (void)loadHostName:(NSString *)hostName onPort:(NSInteger)portNumber { 

     NSString* filePath = [[NSBundle mainBundle] pathForResource:@"ca" ofType:@"crt"]; 

     NSData *iosTrustedCertDerData = [NSData dataWithContentsOfFile:filePath]; 
     certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) iosTrustedCertDerData); 

    NSInputStream *inputStream; 
    NSOutputStream *outputStream; 
    [NSStream qNetworkAdditions_getStreamsToHostNamed:hostName 
               port:portNumber 
              inputStream:&inputStream 
             outputStream:&outputStream]; 


    [inputStream setProperty:NSStreamSocketSecurityLevelTLSv1 forKey:NSStreamSocketSecurityLevelKey]; 
    [outputStream setProperty:NSStreamSocketSecurityLevelTLSv1 forKey:NSStreamSocketSecurityLevelKey]; 

    inputStream.delegate = self; 
    outputStream.delegate = self; 


    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] 
           forMode:NSDefaultRunLoopMode]; 
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] 
           forMode:NSDefaultRunLoopMode]; 


    [inputStream open]; 
    [outputStream open]; 
} 

這代表:

- (void)stream:(NSStream *)aStream 
    handleEvent:(NSStreamEvent)eventCode { 
    SecPolicyRef policy; 
    switch (eventCode) { 
     case NSStreamEventNone: 
      break; 
     case NSStreamEventOpenCompleted: 
      break; 
     case NSStreamEventHasBytesAvailable: 
     { 
      int stop = 1; 
      break; 
     } 
     case NSStreamEventHasSpaceAvailable: 
     { 
      // #1 
      // NO for client, YES for server. In this example, we are a client 
      // replace "localhost" with the name of the server to which you are connecting 
      policy = SecPolicyCreateSSL(NO, CFSTR("192.168.178.14")); 
      SecTrustRef trust = NULL; 
      // #2 
      CFArrayRef streamCertificates = 
      (__bridge CFArrayRef)([aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates]); 
      // #3 
      SecTrustCreateWithCertificates(streamCertificates, 
              policy, 
              &trust); 
      // #4 
      SecTrustSetAnchorCertificates(trust, 
              (__bridge CFArrayRef) [NSArray arrayWithObject:(__bridge id)certificate]);   // #5 
      SecTrustResultType trustResultType = kSecTrustResultInvalid; 
      OSStatus status = SecTrustEvaluate(trust, &trustResultType); 
      if (status == errSecSuccess) { 
       // expect trustResultType == kSecTrustResultUnspecified 
       // until my cert exists in the keychain see technote for more detail. 
       if (trustResultType == kSecTrustResultUnspecified) { 
        NSLog(@"We can trust this certificate! TrustResultType: %d", trustResultType); 
       } else { 
        NSLog(@"Cannot trust certificate. TrustResultType: %d", trustResultType); 
       } 
      } else { 
       NSLog(@"Creating trust failed: %d", status); 
       [aStream close]; 
      } 
      if (trust) { 
       CFRelease(trust); 
      } 
      if (policy) { 
       CFRelease(policy); 
      } 
      break; 
     } 
     case NSStreamEventErrorOccurred: 
      NSLog(@"unexpected NSStreamEventErrorOccurred: %@", [aStream streamError]); 
      break; 
     case NSStreamEventEndEncountered: 
      break; 
     default: 
      break; 
    } 

回答

1

它工作正常:

NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:1]; 
    [settings setObject:(NSString *)NSStreamSocketSecurityLevelTLSv1 forKey:(NSString *)kCFStreamSSLLevel]; 
    [settings setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCFStreamSSLAllowsAnyRoot]; 
    [settings setObject:hostName forKey:(NSString *)kCFStreamSSLPeerName]; 
    [settings setObject:NSStreamSocketSecurityLevelTLSv1 forKey:NSStreamSocketSecurityLevelKey]; 
    inputStream.delegate = self; 
    outputStream.delegate = self; 

    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] 
           forMode:NSDefaultRunLoopMode]; 
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] 

    CFReadStreamSetProperty((CFReadStreamRef)inputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings); 
    CFWriteStreamSetProperty((CFWriteStreamRef)outputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings); 
+1

值得注意的是'kCFStreamSSLAllowsAnyRoot'已被棄用,但它指出了我的正確方向:'kCFStreamSSLValidatesCertificateChain'可以設置爲'@ NO'以在流的SSL協商中獲得類似的效果。 – Tim 2015-07-23 17:13:41