SSL Handshake with my certificate by NSStream

2020-08-04 04:45发布

问题:

I'm writing a client for iOS to connect to my server by SSL/TLS. I have decided to use NSStream implementation. And now my project has been stopped due to SSL Handshake, I don't know how handle it with streams and can't find any examples of delegates with it. As I understood the certificate passing should be in NSStreamEventHasSpaceAvailable, but actually I don't understand how. I need to pass my certificate (he is not installed on device). Can someone help me?

This code crashes with this:

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.)"

What I have:

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

And this delegate:

- (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:

It works fine:

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