I use CocoaAsyncSocket to send data to our server without SSL. Now the server side has implemented SSL / TLS with client authentication. To implement this in our application, I was provided with the following three files:
- cha-chain.cert.pem
- client-test.cert.pem
- client-test.key.pem
I converted the files to readable iOS formats as shown below:
- ca-chain.cert.pem for ca-cert.cer
- client-test.cert.pem for client_cert.cer
- client-test.key.pem for client_key.p12
It works for me before SSL. But there were problems with client authentication.
Here is my code:
didConnectToHost:
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port; { // Configure SSL/TLS settings NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:3]; // Allow self-signed certificates CFArrayRef certsArray = [self loadCertificates]; [settings setObject:@0 forKey:GCDAsyncSocketSSLProtocolVersionMax]; [settings setObject:[NSNumber numberWithBool:YES] forKey:GCDAsyncSocketManuallyEvaluateTrust]; [settings setObject:(id)CFBridgingRelease(certsArray) forKey:GCDAsyncSocketSSLCertificates]; [sock startTLS:settings]; }
didReceiveTrust:
- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler { NSString *caCertPath = [[NSBundle mainBundle] pathForResource:@"ca-cert" ofType:@"cer"]; NSData *caCertData = [NSData dataWithContentsOfFile:caCertPath]; NSString *clientCertPath = [[NSBundle mainBundle] pathForResource:@"client_cert" ofType:@"cer"]; NSData *clientCertData = [NSData dataWithContentsOfFile:clientCertPath]; OSStatus status = -1; SecTrustResultType result = kSecTrustResultDeny; if(caCertData && clientCertData) { SecCertificateRef cert1; cert1 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) caCertData); SecCertificateRef cert2; cert2 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) clientCertData); const void *ref[] = {cert1,cert2}; CFArrayRef ary = CFArrayCreate(NULL, ref, 2, NULL); SecTrustSetAnchorCertificates(trust, ary); status = SecTrustEvaluate(trust, &result); } else { NSLog(@"local certificates could not be loaded"); completionHandler(NO); } if ((status == noErr && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified))) { completionHandler(YES); } else { CFArrayRef arrayRefTrust = SecTrustCopyProperties(trust); NSLog(@"error in connection occured\n%@", arrayRefTrust); completionHandler(NO); } }
loadCertificates:
-(CFArrayRef) loadCertificates { NSString *clientKeyPath = [[NSBundle mainBundle] pathForResource:@"client_key" ofType:@"p12"]; NSData* clientKeyData = [NSData dataWithContentsOfFile:clientKeyPath]; NSLog(@"key : %@",[[NSString alloc] initWithData:clientKeyData encoding:NSASCIIStringEncoding]); CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(clientKeyData); CFStringRef password = CFSTR("_mypassword_"); const void *keys[] = { kSecImportExportPassphrase }; const void *values[] = { password }; CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL); OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items); CFRelease(options); CFRelease(password); if(securityError == errSecSuccess) NSLog(@"Success opening p12 certificate."); CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0); SecIdentityRef myIdent = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity); NSString *clientCertPath = [[NSBundle mainBundle] pathForResource:@"client_cert" ofType:@"cer"]; NSData *clientCertData = [NSData dataWithContentsOfFile:clientCertPath]; SecCertificateRef clientCert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) clientCertData);
I tried client authentication for two days. I get this error:
2017-03-13 15:35:40.777 MPS[79612:1478858] GCDAsyncSocket socketDidDisconnect Error - Error Domain=kCFStreamErrorDomainSSL Code=-9806 "(null)" UserInfo={NSLocalizedRecoverySuggestion=Error code definition can be found in Apple SecureTransport.h}
I read that the CocoaAyncSocket library has a problem with client authentication with manual trust verification. I tried a workaround as mentioned here: Client-side authentication support with manual trust control Still no luck. I could not find what I was missing.
Thanks in advance! -Uma