我一直在讨论一个奇怪的问题 - 这似乎是未说出口的(或者说是完全不同的)。 令人惊讶的是,我找不到一个HTTPS示例 - 我找到的所有内容都有'todo'!
简而言之:
我正在尝试使用有效的证书创建一个使用TLS的“服务器”。
问题:
成功导入证书后,设置GCDAsyncSockets的设置,然后调用startTLS,连接将立即删除 'SecureTransport error -9800' - Chrome称之为'ERR_SSL_PROTOCOL_ERROR'
测试:
Chrome是我选择的测试平台,因为此练习稍后将处理WebSocket流量 - 因此我使用的是有效的证书,而非自签名。
Long desc:
使用GCDAsyncSocket监听端口,然后一旦我收到连接,我就接受那个套接字和'startTLS'来处理它。 (我不是专门使用GCDAsyncSocket,它只是我熟悉它在客户端套接字使用明文和TLS,以及服务器明文 - 它只是服务器TLS让我难倒)。
证书是已知公共CA(Comodo或GoDaddy)的签名证书 - 我已成功使用它.Net 下面的源代码将成功打开证书并报告对等名称。 然而,.Net简化了很多这方面的工作,所以比较起来并不容易。 (加载cert并传入x509对象,然后应用于新套接字。在这里,我还必须定义密码和SSL版本。)
简化代码示例:
- (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];
}
我发现了很多旧的例子,没有一个适用(不使用较旧的GCDAsyncSocket或较旧的Xcode)。 我发现最接近后来的'GCDAsyncSocketSSLProtocolVersionMin / Max'设置为2,例如在Chrome中不支持。
有没有人成功并愿意分享一些指示?
亲切的问候,KF。
答案 0 :(得分:1)
在10分钟后计算出来!
我的更改:
//THIS! Tried prior, but GCDAsyncSocket docs stated not-needed, plus was getting different handshake errors.
settings[(NSString *)kCFStreamSSLIsServer] = [NSNumber numberWithBool:YES];
其他SO用户可能会觉得有用的其他更改:
//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];
非常感谢 - 即使你是在寻找Rubber Ducking!