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