Objective-C:在TLS TCP连接上清除由我们自己的PKI(根CA)签署的服务器证书
* 解决 *
我的问题是参考以下问题:
Objective-C:如何用签名者的公钥validationSecCertificateRef?
我们有自己的PKI,所以我们信任自己的rootCA。 通过这个rootCA,我们签署了传递给个人服务器的证书。 现在我想连接iOS应用程序,并检查从服务器传递的证书是否与我们的CA签名。
我的应用程序应该能够使用由GCDAsyncSocket
build立的TCP连接来连接到具有此证书的n台服务器 (可能使用零configuration服务)。 我有我的应用程序中的证书的公共部分,我想添加到我的“CertChain”,所以应用程序将信任他们连接。
我尝试了很多,但是我仍然无法通过SecTrustEvaluate(trust, &result);
有效的结果。 (我想在生产中使用它,所以请不要告诉我有关停用validation的任何事情)
我的证书:
在app:rootCA,oldServerCA(cer)
在服务器上(通过信任):homeServer,oldServer
我的证书链:
根CA签署的家庭服务器
oldServerCA签署了oldServer
我的代码部分:
添加更新
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port; { // Configure SSL/TLS settings NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:3]; // Allow self-signed certificates [settings setObject:[NSNumber numberWithBool:YES] forKey:GCDAsyncSocketManuallyEvaluateTrust]; [sock startTLS:settings]; // get the certificates as data for further operations NSString *certFilePath1 = [[NSBundle mainBundle] pathForResource:@"rootCA" ofType:@"cer"]; // also tried it with 'der', same result NSData *certData1 = [NSData dataWithContentsOfFile:certFilePath1]; NSString *certFilePath2 = [[NSBundle mainBundle] pathForResource:@"oldServerCA" ofType:@"cer"]; NSData *certData2 = [NSData dataWithContentsOfFile:certFilePath2]; // if data exists, use it if(certData1 && certData2) { SecCertificateRef cert1; cert1 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData1); SecCertificateRef cert2; cert2 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData2); // only working for "cer" NSString *name = [NSString stringWithUTF8String:CFStringGetCStringPtr(SecCertificateCopySubjectSummary(cert1), kCFStringEncodingUTF8)]; // maybe I understood the usage of "name" in "kSecAttrApplicationTag" wrong? OSStatus status = SecItemAdd((__bridge CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)(kSecClassKey), kSecClass, (__bridge id)kSecAttrKeyTypeRSA, kSecAttrKeyType, (__bridge id)kSecAttrKeyClassPublic, kSecAttrKeyClass, kCFBooleanTrue, kSecAttrIsPermanent, [name dataUsingEncoding:NSUTF8StringEncoding], kSecAttrApplicationTag, certData1, kSecValueData, kCFBooleanTrue, kSecReturnPersistentRef, nil], NULL); //don't need public key ref // Setting "cer" is successfully and delivers "noErr" in first run, then "errKCDuplicateItem" NSLog(@"evaluate with status %d", (int)status); NSString *name2 = [NSString stringWithUTF8String:CFStringGetCStringPtr(SecCertificateCopySubjectSummary(cert2), kCFStringEncodingUTF8)]; OSStatus status2 = SecItemAdd((__bridge CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)(kSecClassKey), kSecClass, (__bridge id)kSecAttrKeyTypeRSA, kSecAttrKeyType, (__bridge id)kSecAttrKeyClassPublic, kSecAttrKeyClass, kCFBooleanTrue, kSecAttrIsPermanent, [name2 dataUsingEncoding:NSUTF8StringEncoding], kSecAttrApplicationTag, certData2, kSecValueData, kCFBooleanTrue, kSecReturnPersistentRef, nil], NULL); //don't need public key ref NSLog(@"evaluate with status %d", (int)status2); // log here -> certificates were loaded. Fine // create references of each to proof them seperatly const void *ref[] = {cert1}; CFArrayRef aryRef = CFArrayCreate(NULL, ref, 1, NULL); const void *ref2[] = {cert2}; CFArrayRef aryRef2 = CFArrayCreate(NULL, ref2, 1, NULL); // need this way to get sock.sslContext, otherways it's NULL (see implementation of GCDAsyncSocket) [sock performBlock:^{ SSLContextRef sslContext = sock.sslContext; OSStatus status = SSLSetCertificate(sslContext, aryRef); // the status is everywhere always -909 -> badReqErr /*bad parameter or invalid state for operation*/ if(status == noErr) NSLog(@"successfully set ssl certificates"); else NSLog(@"setting ssl certificates failed"); status = SSLSetCertificate(sock.sslContext, aryRef2); if(status == noErr) NSLog(@"successfully set ssl certificates"); else NSLog(@"setting ssl certificates failed"); status = SSLSetEncryptionCertificate(sock.sslContext, aryRef); if(status == noErr) NSLog(@"successfully set ssl certificates"); else NSLog(@"setting ssl certificates failed"); }]; } @synchronized( self ) { if( isConnected == NO ) { if(gcdAsyncSocket && [gcdAsyncSocket isConnected]) { isConnected = YES; [gcdAsyncSocket readDataWithTimeout:READ_TIMEOUT tag:0]; [NSThread detachNewThreadSelector:@selector(readDataToData:withTimeout:tag:) toTarget:gcdAsyncSocket withObject:nil]; [gcdAsyncSocket readDataToData:[GCDAsyncSocket LFData] withTimeout:READ_TIMEOUT tag:0]; [del onConnect]; } } } }
以及…如果不在这里工作,然后手动检查…
- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler { // https://code.csdn.net/OS_Mirror/CocoaAsyncSocket/file_diff/a4b9c4981b3c022ca89d0cdaadecc70b825ad4f1...5d58af30d2d8a3e0f7219487e72f1b4b2c3b4894/GCD/Xcode/SimpleHTTPClient/Desktop/SimpleHTTPClient/SimpleHTTPClientAppDelegate.m dispatch_queue_t bgQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(bgQueue, ^{ // This is where you would (eventually) invoke SecTrustEvaluate. // Presumably, if you're using manual trust evaluation, you're likely doing extra stuff here. // For example, allowing a specific self-signed certificate that is known to the app. NSString *certFilePath1 = [[NSBundle mainBundle] pathForResource:@"rootCA" ofType:@"cer"]; NSData *certData1 = [NSData dataWithContentsOfFile:certFilePath1]; NSString *certFilePath2 = [[NSBundle mainBundle] pathForResource:@"oldServerCA" ofType:@"cer"]; NSData *certData2 = [NSData dataWithContentsOfFile:certFilePath2]; if(certData1 && certData2) { CFArrayRef arrayRefTrust = SecTrustCopyProperties(trust); SecTrustResultType result = kSecTrustResultUnspecified; // usualy should work already here OSStatus status = SecTrustEvaluate(trust, &result); NSLog(@"evaluate with result %d and status %d", result, (int)status); NSLog(@"trust properties: %@", arrayRefTrust); /* log: evaluate with result 5 and status 0 trust properties: ( { type = error; value = "Root certificate is not trusted."; // expected, when top part was not working } */ SecCertificateRef cert1; cert1 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData1); SecCertificateRef cert2; cert2 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData2); const void *ref[] = {cert1}; CFIndex count = SecTrustGetCertificateCount(trust); // CFMutableArrayRef aryRef = CFArrayCreateMutable(NULL, count + 1, NULL); // CFArrayAppendValue(aryRef, ref); CFArrayCreate(NULL, ref, 2, NULL); // # # # # // so check one by one... BOOL isMatching = NO; for (int i = 0; i < count; i++) { SecCertificateRef certRef = SecTrustGetCertificateAtIndex(trust, i); NSString *name = [NSString stringWithUTF8String:CFStringGetCStringPtr(SecCertificateCopySubjectSummary(certRef), kCFStringEncodingUTF8)]; // only working for "cer" NSLog(@"remote cert at index %d is '%@'", i, name); /* first is 'homeserver', second is 'oldServer' */ // const void *ref[] = {certRef, cert1, cert2}; // CFArrayRef aryCheck = CFArrayCreate(NULL, ref, 3, NULL); // check against the new cert (rootCA) const void *ref[] = {certRef, cert1}; CFArrayRef aryCheck = CFArrayCreate(NULL, ref, 2, NULL); SecTrustRef trustManual; OSStatus certStatus = SecTrustCreateWithCertificates(aryCheck, SecPolicyCreateBasicX509(), &trustManual); // certStatus always noErr NSLog(@"certStatus: %d", (int)certStatus); SecTrustResultType result; OSStatus status = SecTrustEvaluate(trustManual, &result); CFArrayRef arrayRef = SecTrustCopyProperties(trustManual); NSLog(@"evaluate with result %d and status %d", result, (int)status); NSLog(@"trust properties: %@", arrayRef); /* log: evaluate with result 5 and status 0 trust properties: ( { type = error; value = "Root certificate is not trusted."; } */ // always else-part because result is "kSecTrustResultRecoverableTrustFailure" if (status == noErr && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)) { isMatching = YES; NSLog(@"certificates matches"); } else { NSLog(@"certificates differs"); } } if (isMatching || (status == noErr && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified))) { completionHandler(YES); } else { completionHandler(NO); } } completionHandler(NO); }); }
更新1
去除
[settings setObject:[NSNumber numberWithBool:YES] forKey:GCDAsyncSocketManuallyEvaluateTrust];
现在使用
SecCertificateRef cert1, cert2; // init certs, see top part // according to @SeanBaker "Certs[0] would be nil (you don't want to do client auth), and certs[1...] would be the root certificates you want to trust in establishing the connection" const void *certs[] = {NULL, cert1, cert2}; // const void *certs[] = {nil, cert1, cert2}; CFArrayRef aryCerts = CFArrayCreate(NULL, certs, 3, NULL); [settings setObject:(__bridge NSArray*)aryCerts forKey:(NSString *)kCFStreamSSLCertificates];
但得到OSStatus -50( /*error in user parameter list*/
)
// 2. kCFStreamSSLCertificates value = [tlsSettings objectForKey:(NSString *)kCFStreamSSLCertificates]; if ([value isKindOfClass:[NSArray class]]) { CFArrayRef certs = (__bridge CFArrayRef)value; status = SSLSetCertificate(sslContext, certs); ...
似乎我用错了,但我没有看到错误:/(不经常使用核心基础)
如果您需要进一步的信息,请询问。 每一个提示可以拯救生命:)
我自己使用自定义证书来validation消息处理应用程序在开发模式下使用的多个服务器。
如果您有权访问p12(包含私钥和因此签署的身份)文件,则可以使用kCFStreamSSLCertificatesvalidation服务器证书
否则(在公钥的情况下),您可以select通过对等名称kCFStreamSSLPeerName进行validation。
在你的代码片段中,你所做的一件事情不正确的是你如何向GCDAsyncSocket模块提供证书。 并因此find你提到的错误。
正确的方法如下所示:
NSArray *myCerts = [[NSArray alloc] initWithObjects:(__bridge id)identity1, (__bridge id)myReturnedCertificate1, nil]; [settings setObject:myCerts forKey:(NSString *)kCFStreamSSLCertificates];
按照Apple文档标识,在使用kCFStreamSSLCertificates时是必需的:
您必须在certRefs [0]中放置一个SecIdentityRef对象,该对象标识叶证书及其相应的私钥。 指定根证书是可选的;
完整的细节:
以下是使用自定义签名CA证书时要遵循的步骤。 请注意:示例基于GCDAsyncSocket
- 将公共部分证书保存在应用程序资源包中。
- 阅读上述证书并将证书添加到钥匙串
- 实现委托function –
(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port;
在这个函数中提供你的证书给GCDAsyncSocket
NSArray *myCerts = [[NSArray alloc] initWithObjects:(__bridge id)identity1, (__bridge id)myReturnedCertificate1, nil]; [settings setObject:myCerts forKey:(NSString *)kCFStreamSSLCertificates];
根据你想要手动validation信任的基础上使用YES(不推荐)或NO以下?
[settings setObject:[NSNumber numberWithBool:YES] forKey:GCDAsyncSocketManuallyEvaluateTrust];
- 如果您select手动validation信任,请覆盖以下委托方法。
(void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler 🙁 void(^)(BOOL shouldTrustPeer))completionHandler
在此function中,您应该阅读信任中的所有证书,并尝试与您随应用程序一起提供的证书进行匹配。
示例代码:
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port; { // Configure SSL/TLS settings NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:3]; // get the certificates as data for further operations SecIdentityRef identity1 = nil; SecTrustRef trust1 = nil; NSData *certData1 = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"[Dev] InHouse_Certificates" ofType:@"p12"]]; CFDataRef myCertData1 = (__bridge_retained CFDataRef)(certData1); [self extractIdentityAndTrust:myCertData1 withIdentity:&identity1 withTrust:&trust1 withPassword:CFSTR("1234")]; NSString* summaryString1 = [self copySummaryString:&identity1]; SecIdentityRef identity2 = nil; SecTrustRef trust2 = nil; NSData *certData2 = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"[Dis] InHouse_Certificates" ofType:@"p12"]]; CFDataRef myCertData2 = (__bridge_retained CFDataRef)(certData2); [self extractIdentityAndTrust:myCertData2 withIdentity:&identity2 withTrust:&trust2 withPassword:CFSTR("1234")]; NSString* summaryString2 = [self copySummaryString:&identity2]; // if data exists, use it if(myCertData1 && myCertData2) { //Delete if already exist. Just temporary SecItemDelete((__bridge CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)(kSecClassKey), kSecClass, (__bridge id)kSecAttrKeyTypeRSA, kSecAttrKeyType, (__bridge id)kSecAttrKeyClassPublic, kSecAttrKeyClass, kCFBooleanTrue, kSecAttrIsPermanent, [summaryString1 dataUsingEncoding:NSUTF8StringEncoding], kSecAttrApplicationTag, certData1, kSecValueData, kCFBooleanTrue, kSecReturnPersistentRef, nil]); OSStatus status1 = SecItemAdd((__bridge CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)(kSecClassKey), kSecClass, (__bridge id)kSecAttrKeyTypeRSA, kSecAttrKeyType, (__bridge id)kSecAttrKeyClassPublic, kSecAttrKeyClass, kCFBooleanTrue, kSecAttrIsPermanent, [summaryString1 dataUsingEncoding:NSUTF8StringEncoding], kSecAttrApplicationTag, certData1, kSecValueData, kCFBooleanTrue, kSecReturnPersistentRef, nil], NULL); //don't need public key ref // Setting "cer" is successfully and delivers "noErr" in first run, then "errKCDuplicateItem" NSLog(@"evaluate with status %d", (int)status1); //Delete if already exist. Just temporary SecItemDelete((__bridge CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)(kSecClassKey), kSecClass, (__bridge id)kSecAttrKeyTypeRSA, kSecAttrKeyType, (__bridge id)kSecAttrKeyClassPublic, kSecAttrKeyClass, kCFBooleanTrue, kSecAttrIsPermanent, [summaryString2 dataUsingEncoding:NSUTF8StringEncoding], kSecAttrApplicationTag, certData2, kSecValueData, kCFBooleanTrue, kSecReturnPersistentRef, nil]); //NSString *name2 = [NSString stringWithUTF8String:CFStringGetCStringPtr(SecCertificateCopySubjectSummary(cert2), kCFStringEncodingUTF8)]; OSStatus status2 = SecItemAdd((__bridge CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)(kSecClassKey), kSecClass, (__bridge id)kSecAttrKeyTypeRSA, kSecAttrKeyType, (__bridge id)kSecAttrKeyClassPublic, kSecAttrKeyClass, kCFBooleanTrue, kSecAttrIsPermanent, [summaryString2 dataUsingEncoding:NSUTF8StringEncoding], kSecAttrApplicationTag, certData2, kSecValueData, kCFBooleanTrue, kSecReturnPersistentRef, nil], NULL); //don't need public key ref NSLog(@"evaluate with status %d", (int)status2); SecCertificateRef myReturnedCertificate1 = NULL; OSStatus status3 = SecIdentityCopyCertificate (identity1, &myReturnedCertificate1); SecCertificateRef myReturnedCertificate2 = NULL; OSStatus status4 = SecIdentityCopyCertificate (identity2, &myReturnedCertificate2); NSArray *myCerts = [[NSArray alloc] initWithObjects:(__bridge id)identity1, (__bridge id)myReturnedCertificate1, nil]; [settings setObject:myCerts forKey:(NSString *)kCFStreamSSLCertificates]; // Allow self-signed certificates [settings setObject:[NSNumber numberWithBool:YES] forKey:GCDAsyncSocketManuallyEvaluateTrust]; [sock startTLS:settings]; } }
如果由于某种原因您决定手动评估信任。
- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler { dispatch_queue_t bgQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(bgQueue, ^{ // This is where you would (eventually) invoke SecTrustEvaluate. SecIdentityRef identity1 = nil; SecTrustRef trust1 = nil; NSData *certData1 = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"[Dev] InHouse_Certificates" ofType:@"p12"]]; CFDataRef myCertData1 = (__bridge_retained CFDataRef)(certData1); [self extractIdentityAndTrust:myCertData1 withIdentity:&identity1 withTrust:&trust1 withPassword:CFSTR("1234")]; SecIdentityRef identity2 = nil; SecTrustRef trust2 = nil; NSData *certData2 = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"[Dis] InHouse_Certificates" ofType:@"p12"]]; CFDataRef myCertData2 = (__bridge_retained CFDataRef)(certData2); [self extractIdentityAndTrust:myCertData2 withIdentity:&identity2 withTrust:&trust2 withPassword:CFSTR("1234")]; if(myCertData1 && myCertData2) { CFArrayRef arrayRefTrust = SecTrustCopyProperties(trust); SecTrustResultType result = kSecTrustResultUnspecified; // usualy should work already here OSStatus status = SecTrustEvaluate(trust, &result); NSLog(@"evaluate with result %d and status %d", result, (int)status); NSLog(@"trust properties: %@", arrayRefTrust); /* log: evaluate with result 5 and status 0 trust properties: ( { type = error; value = "Root certificate is not trusted."; // expected, when top part was not working } */ SecCertificateRef myReturnedCertificate1 = NULL; OSStatus status3 = SecIdentityCopyCertificate (identity1, &myReturnedCertificate1); SecCertificateRef myReturnedCertificate2 = NULL; OSStatus status4 = SecIdentityCopyCertificate (identity2, &myReturnedCertificate2); const void *ref[] = {myReturnedCertificate1}; CFIndex count = SecTrustGetCertificateCount(trust); // CFMutableArrayRef aryRef = CFArrayCreateMutable(NULL, count + 1, NULL); // CFArrayAppendValue(aryRef, ref); CFArrayCreate(NULL, ref, 2, NULL); // # # # # // so check one by one... BOOL isMatching = NO; for (int i = 0; i < count; i++) { SecCertificateRef certRef = SecTrustGetCertificateAtIndex(trust, i); NSString *name = [NSString stringWithUTF8String:CFStringGetCStringPtr(SecCertificateCopySubjectSummary(certRef), kCFStringEncodingUTF8)]; NSLog(@"remote cert at index %d is '%@'", i, name); const void *ref[] = {certRef, myReturnedCertificate1}; CFArrayRef aryCheck = CFArrayCreate(NULL, ref, 2, NULL); SecTrustRef trustManual; OSStatus certStatus = SecTrustCreateWithCertificates(aryCheck, SecPolicyCreateBasicX509(), &trustManual); // certStatus always noErr NSLog(@"certStatus: %d", (int)certStatus); SecTrustResultType result; OSStatus status = SecTrustEvaluate(trustManual, &result); CFArrayRef arrayRef = SecTrustCopyProperties(trustManual); NSLog(@"evaluate with result %d and status %d", result, (int)status); NSLog(@"trust properties: %@", arrayRef); /* log: evaluate with result 5 and status 0 trust properties: ( { type = error; value = "Root certificate is not trusted."; } */ if (status == noErr && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)) { isMatching = YES; NSLog(@"certificates matches"); } else { NSLog(@"certificates differs"); } } if (isMatching || (status == noErr && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified))) { completionHandler(YES); } else { completionHandler(NO); } } completionHandler(NO); }); }
更新:
根据Apple文档:
您必须在certRefs [0]中放置一个SecIdentityRef对象,该对象标识叶证书及其相应的私钥。 指定根证书是可选的;
正如苹果build议的那样,如果您使用in.cer格式的证书,则应该使用对等域名(完全限定的域名)来匹配两个证书。
您可以使用此function来validation对等方的证书中的通用名称字段。 如果您调用此函数,并且证书中的公用名称与您在peerName参数中指定的值不匹配,则握手失败并返回errSSLXCertChainInvalid。 使用这个function是可选的。
通过在手动检查中设置证书为信任的anchorCertificates来解决这个问题- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler
但是感谢你提示和努力:)会给你一些这样的赏金。
NSString *certFilePath1 = [[NSBundle mainBundle] pathForResource:@"rootCA" ofType:@"cer"]; NSData *certData1 = [NSData dataWithContentsOfFile:certFilePath1]; NSString *certFilePath2 = [[NSBundle mainBundle] pathForResource:@"oldServerCA" ofType:@"cer"]; NSData *certData2 = [NSData dataWithContentsOfFile:certFilePath2]; OSStatus status = -1; SecTrustResultType result = kSecTrustResultDeny; if(certData1 && certData2) { SecCertificateRef cert1; cert1 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData1); SecCertificateRef cert2; cert2 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData2); 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); }
为什么要手动评估信任? 您可以将您的CA证书设置为GCDAsyncSocket在SSL设置中评估的唯一受信任的根,并让它为您执行validation?
在这样的模型中,您将(1)减less自己的编码工作[和风险](2)只信任由您的私人CA为这个连接签署的证书[相对于默认信任存储中的公共CA]。