Objective-C:在TLS TCP连接上生成由我们自己的PKI(根CA)签名的服务器证书

时间:2015-02-09 10:17:23

标签: ios objective-c tcp self-signed gcdasyncsocket

*的解决 *

我的问题是参考以下问题:
Objective-C: How to verify SecCertificateRef with signer's public key?

我们拥有自己的PKI,因此我们信任自己的rootCA。使用此rootCA,我们将签署提供给个人服务器的证书。现在我想连接iOS应用程序并检查从服务器提供的证书是否已通过我们的CA签名。

我的应用应该能够使用由GCDAsyncSocket建立的TCP连接连接到具有此证书的n个服务器(可能使用零配置服务找到)。我在我的应用程序中有公共部分证书,我想添加到我的“CertChain”,以便应用程序在连接时信任它们。

我已经尝试了很多,但我仍然无法通过SecTrustEvaluate(trust, &result);传递有效结果。 (我想在高效中使用它,所以请不要告诉我有关停用验证的任何信息)

我的证书:
在app中:rootCA,oldServerCA(cer)
在服务器上(通过信任):homeServer,oldServer

我的证书链:
rootCA签署了homeServer
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);
...

好像我用错了,但我没有看到错误:/(不经常使用核心基础)

如果您需要更多信息,请询问。每个提示都可以拯救生命:)

4 个答案:

答案 0 :(得分:6)

我自己使用自定义证书来验证我们的消息传递应用程序在开发模式下使用的多个服务器。

如果您有权访问p12(包含私钥,因此签名身份)文件,则可以使用kCFStreamSSLCertificates验证服务器证书

否则(仅在公钥的情况下)您可以选择通过对等名称kCFStreamSSLPeerName进行验证。

在您的代码段中,您做错的一件事是如何将证书提供给GCDAsyncSocket模块。因此找到你提到的错误。

正确的方法如下:

    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

  1. 将您的公共部分证书保留在应用程序资源包中。
  2. 阅读以上证书并将证书添加到钥匙串
  3. 实施委托功能 -
  4.   

    (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host   端口:(uint16_t)端口;

    在此函数中,将您的证书提供给GCDAsyncSocket

        NSArray *myCerts = [[NSArray alloc] initWithObjects:(__bridge id)identity1, (__bridge id)myReturnedCertificate1, nil];
        [settings setObject:myCerts forKey:(NSString *)kCFStreamSSLCertificates];
    

    根据您想要手动验证信任的情况,使用YES(不推荐)或NO,如下所示?

       [settings setObject:[NSNumber numberWithBool:YES]
                     forKey:GCDAsyncSocketManuallyEvaluateTrust];
    
    1. 如果您选择手动验证信任,请覆盖以下委托方法。
    2.   

      (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust   completionHandler:(void(^)(BOOL shouldTrustPeer))completionHandler

      在此功能中,您应该阅读信任中的所有证书,并尝试与您随应用程序提供的证书一起使用。

      示例代码:


      - (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对象   叶证书及其对应的私钥。指定一个   根证书是可选的;

      根据Apple的建议,如果您使用的是证书in.cer格式,则应使用对等域名(完全限定的域名)匹配两个证书。

        

      您可以使用此功能验证中的公用名字段   同行的证书。如果你调用这个函数和公用名   证书与您在peerName中指定的值不匹配   参数,然后握手失败并返回errSSLXCertChainInvalid。   使用此功能是可选的。

答案 1 :(得分:3)

通过在手动检查- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler中将证书设置为信任的anchorCertificates来解决问题,但感谢您的提示和努力:)将为您提供一些赏金。

    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);
    }

答案 2 :(得分:0)

为什么要手动评估信任?您是否可以将CA证书设置为GCDAsyncSocket在SSL设置中评估的唯一受信任的根,并让它为您进行验证?

在这样的模型中,您将(1)减少您自己的编码工作量[和风险](2)仅信任您的私有CA为此连接签署的证书[vs还信任默认信任库中的公共CA]

答案 3 :(得分:0)

我只是想将其提供给今天看过此内容的任何人-我创建了一个程序包来帮助iOS上使用新的iOS 13限制的TLS。放在这里以防万一。随时贡献力量:

https://github.com/eamonwhiter73/IOSObjCWebSockets