Reputation: 11
I've been racking over an odd issue - something that appears to be unspoken of (or termed quite differently). Suprisingly, I cannot find a HTTPS example - everything I find has this 'todo'!
In brief:
I'm trying to create a 'server' that uses TLS, using a valid cert.
Issue:
Once I import certificate successfully, setup settings for GCDAsyncSockets, then call startTLS, the connection will immediately drop with 'SecureTransport error -9800' - Chrome calls this 'ERR_SSL_PROTOCOL_ERROR'
Test:
Chrome is my chosen test-bed as this exercise will later handle WebSocket traffic - thus I use a valid cert, not self-signed.
Long desc:
Using GCDAsyncSocket to listen on a port, then once I receive a connection, I take that socket and 'startTLS' to handle it. (I'm not exclusive to using GCDAsyncSocket, it's simply that I'm familiar with it in client socket use cleartext & TLS, and server cleartext - it's only server TLS getting me stumped).
The certificate is a signed cert with a known public CA (Comodo or GoDaddy) - I use it already successfully in .Net The source below will successfully open the cert and report back the peername. .Net however, simplifies a lot of this, so not trivial to compare. (load cert with pass into an x509 object, then apply to the new socket. Whereas here, I have to define ciphers and SSL versions too).
Simplified code example:
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
//Get certificate
NSString* p12Path = [[NSBundle bundleForClass:[self class]] pathForResource:@"mySignedCert"
ofType:@"pfx"];
CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain([[NSData alloc] initWithContentsOfFile:p12Path]);
CFStringRef password = CFSTR("myCertPassword"); //Cert Password
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."); //All good!
else
return nil;
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef myIdent = (SecIdentityRef)CFDictionaryGetValue(identityDict,
kSecImportItemIdentity);
SecIdentityRef certArray[1] = { myIdent };
CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
//Build TLS settings dictionary
[self willSecureWithSettings:settings]; //This just uses SSLGetNumberSupportedCiphers & SSLGetSupportedCiphers to create a cipher array and appends to settings
settings[(NSString *)kCFStreamSSLCertificates] = (__bridge id)(myCerts);
settings[(NSString *)kCFStreamSSLPeerName] = @"my.peer.address";
/* Valid values for protocol types
kSSLProtocolUnknown = 0, no protocol negotiated/specified; use default
kSSLProtocol3 = 2, SSL 3.0
kTLSProtocol1 = 4, TLS 1.0
kTLSProtocol11 = 7, TLS 1.1
kTLSProtocol12 = 8, TLS 1.2
kDTLSProtocol1 = 9, DTLS 1.0
*/
//Tried 0,2,4,7,8 - 9 throws another error, possibly unsupported.
settings[GCDAsyncSocketSSLProtocolVersionMin] = [NSNumber numberWithInteger:8];
settings[GCDAsyncSocketSSLProtocolVersionMax] = [NSNumber numberWithInteger:8];
[newSocket startTLS:settings];
[newSocket readDataWithTimeout:-1 tag:0];
}
I've found many old examples, none are applicable anymore (without using an older GCDAsyncSocket or older Xcode). The closest I found made mention of the later 'GCDAsyncSocketSSLProtocolVersionMin/Max' set to 2 which is unsupported in Chrome for example.
Has anyone been successful and willing to share some pointers?
Kind regards, KF.
Upvotes: 0
Views: 526
Reputation: 11
Wow - I spent a decent time working this out, then reached out here for tips.
Figured it 10 mins after!
My changes:
//THIS! Tried prior, but GCDAsyncSocket docs stated not-needed, plus was getting different handshake errors.
settings[(NSString *)kCFStreamSSLIsServer] = [NSNumber numberWithBool:YES];
Other changes that other SO users may find useful:
//Apparently prefereable
settings[(NSString*)kCFStreamPropertyShouldCloseNativeSocket] = [NSNumber numberWithBool:YES];
//Above example was both matching for test. 4-8 was always preferable.
settings[GCDAsyncSocketSSLProtocolVersionMin] = [NSNumber numberWithInteger:4];
settings[GCDAsyncSocketSSLProtocolVersionMax] = [NSNumber numberWithInteger:8];
Thanks SO - even if you were for Rubber Ducking!
Upvotes: 1