Upload p12 file using NSStream

i am having problem while using NSStream api for SSL Connection, i am getting 9806 error whenever i connect to SSL Server i am using valid p12 file. Please let me know what could be the problem?.

NSString *path = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];

NSData *certData = [[NSData alloc] initWithContentsOfFile:path];

   NSString*host = @"some host ip";

   CFStreamCreatePairWithSocketToHost(NULL,                      (__bridge CFStringRef)host, 12345,                      &readStream, &writeStream);

   CFArrayRef keyref = NULL;    OSStatus sanityChesk = SecPKCS12Import((__bridge CFDataRef)certData,                        (__bridge CFDictionaryRef)[NSDictionary                                     dictionaryWithObject:@"123456"                                     forKey:(__bridge id)kSecImportExportPassphrase],                        &keyref);

   if (sanityChesk != noErr) {

     NSLog(@"Error while importing pkcs12 [%d]", (int)sanityChesk);    }  else     NSLog(@"Success opening p12 certificate.");

   CFDictionaryRef identityDict= CFArrayGetValueAtIndex(keyref, 0);

   SecIdentityRef identityRef = (SecIdentityRef)CFDictionaryGetValue(identityDict,                                     kSecImportItemIdentity);

   SecCertificateRef cert = NULL;     OSStatus status = SecIdentityCopyCertificate(identityRef, &cert);

    if (status)

     NSLog(@"SecIdentityCopyCertificate failed.");

   NSArray *myCerts = [[NSArray alloc] initWithObjects:(__bridge id)identityRef, (__bridge id)cert, nil];

   NSMutableDictionary *settings =[[NSMutableDictionary alloc]init];

   [settings setObject:(NSString *)NSStreamSocketSecurityLevelNegotiatedSSL forKey:(NSString *)kCFStreamSSLLevel];

   [settings setObject:[NSNumber numberWithBool:NO] forKey:(NSString *)kCFStreamSSLIsServer];

   [settings setObject:[NSNumber numberWithBool:NO] forKey:(NSString *)kCFStreamSSLValidatesCertificateChain];

   [settings setObject:(NSString *)NSStreamSocketSecurityLevelNegotiatedSSL forKey:(NSString *)kCFStreamSSLLevel];   

[settings setObject:myCerts forKey:(NSString *)kCFStreamSSLCertificates];

   [settings setObject:@"some host ip :12345" forKey:(NSString *)kCFStreamSSLPeerName];

   CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket,                kCFBooleanTrue);

   CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket,                 kCFBooleanTrue);

   CFReadStreamSetProperty((CFReadStreamRef)readStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);

   CFWriteStreamSetProperty((CFWriteStreamRef)writeStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);

 NSInputStream *  inputStream = (__bridge_transfer NSInputStream*)readStream;

   inputStream.delegate = self;

   NSOutputStream * outputStream = (__bridge_transfer NSOutputStream*)writeStream;

   outputStream.delegate = self;

   [inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];

   [outputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];

   CFReadStreamSetProperty((CFReadStreamRef)inputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);

   CFWriteStreamSetProperty((CFWriteStreamRef)outputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);

   [inputStream setProperty: settings forKey: (NSString *)kCFStreamPropertySSLSettings];

   [outputStream setProperty: settings forKey: (NSString *)kCFStreamPropertySSLSettings];

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

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

   [outputStream open];

   [inputStream open];

It would help if you put your code examples in a code block (delimited with triple backquotes, or just click the Code Block button. Right now it’s super hard to read.

The error you’re seeing, -9806, or errSSLClosedAbort, indicates that the remote end dropped the connection. It’s hard to be sure why it did that without knowing more about the server.

Looking at your code, you seem to be setting many different properties redundantly, which is very confusing:

  • You don’t need to set TLS settings on both streams. They act as a pair, so the settings you apply to one stream will also apply to the other.

  • You are mixing CF- and NS-level settings. That’s unnecessary. In your case you need to use a CF-level setting, namely kCFStreamPropertySSLSettings so you can add a client identity, and so you should just stick to those.

  • When it comes to kCFStreamSSLCertificates, you must populate the first item of the array with your identity, which is what you’re currently doing. You don’t need to mess around with subsequent items unless your server requires them. Moreover, these values are meant to be intermediate certificates. Setting the second value to the certificate from the identity is pointless.

  • You are explicitly setting many values, like kCFStreamSSLIsServer, to values that are documented to be their default. This just confuses the issue. Remove such settings.

You are disabling server trust evaluation. kCFStreamSSLValidatesCertificateChain. Why is that?

It looks like you’re connecting to an IP address rather a DNS name. That puts you well off the TLS beaten path. Why are you doing that?

What platform are you working on?

What is your deployment target? This matters because CFSocketStream is not officially deprecated in favour of Network framework. If your deployment target allows the use of Network framework, you should use that rather than messing around with this deprecated API.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Upload p12 file using NSStream
 
 
Q