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

Posted

技术标签:

【中文标题】Objective-C:评估由我们自己的 PKI(根 CA)在 TLS TCP 连接上签名的服务器证书【英文标题】:Objective-C: eveluate server certificate signed by our own PKI (root CA) on TLS TCP connection 【发布时间】:2015-02-09 10:17:22 【问题描述】:

*已解决*

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

我们有自己的 PKI 和我们信任的自己的 rootCA。使用这个 rootCA,我们签署了交付给个人服务器的证书。现在我想连接 ios 应用程序并检查从服务器传递的证书是否使用我们的 CA 签名。

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

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

我的证书: 在应用程序中: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);
...

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

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

【问题讨论】:

【参考方案1】:

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

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

否则(如果只是公钥)您可以选择通过对等名称 kCFStreamSSLPeerName 进行验证。

在您的代码 sn-p 中,您做错的一件事是您如何将证书提供给 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

    将您的公共部分证书保留在应用程序资源包中。 读取上述证书并将证书添加到钥匙串中 实现委托功能-

(void)soc​​ket:(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];

根据您是否要手动验证信任,在下方使用“是”(不推荐)或“否”?

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

(void)soc​​ket:(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 的建议,如果您使用 .cer 格式的证书,则应使用对等域名(完全限定域名)匹配两个证书。

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

【讨论】:

感谢您的详细回答 :) 我会看看如何在我的情况下使用它,因为我没有使用 p12 证书(没有客户端身份验证,只是验证服务器)。 仍然失败,状态为 -50。我应该如何调用` NSArray *myCerts = [[NSArray alloc] initWithObjects:(__bridge id)identity1, (__bridge id)myReturnedCertificate1, nil]; [settings setObject:myCerts forKey:(NSString *)kCFStreamSSLCertificates];` 看起来,如果我不使用 p12 证书并且不必对客户端进行身份验证(nilNULL for (__bridge id)identity1 不起作用) 在我的回答中附加了相关信息。您不能将身份保留为 nil。如果您需要代码示例,将添加。 我没有 SecIdentityRef 也没有 peername,因为每个服务器都可以有自己的名字。所以检查这在大多数情况下会以假结束。看来,我必须使用其他选项。或者你知道不提供身份和密码、持有私钥等情况下通过 cer 的方法 嗨@bllakjakk,我已经按照你的回答和OP的回答,仍然得到错误-9806。你能看看这个线程吗?谢谢! ***.com/questions/42773518/…【参考方案2】:

通过在手动检查- (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);
    

【讨论】:

嗨@geo,我关注了你的帖子和一些我无法解决的问题。你能看看这个线程吗? ***.com/questions/42773518/… 提前致谢! 嗨@Uma。那是不久前的事了,但据我记得,对于客户端身份验证,您需要另一种方法来验证这部分内容。我实际上忙于投入更多时间,对不起:/ 感谢您的回复。!我会看看我能找到什么。 :)【参考方案3】:

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

在这样的模型中,您将 (1) 减少您自己的编码工作 [和风险] (2) 仅信任由您的私有 CA 签署的证书用于此连接 [与默认信任存储中的公共 CA 相比] .

【讨论】:

你有提示吗,具体怎么做?示例或链接? 您只需将 kCFStreamSSLCertificates 添加到您的设置字典中。 Certs[0] 将为 nil(您不想进行客户端身份验证),而 certs[1...] 将是您在建立连接时要信任的根证书。您可以阅读有关 Apple OS 文档中的设置的更多信息:opensource.apple.com/source/libsecurity_ssl/…;只需搜索 SSLSetCertificate()。 GCDAsyncSocket 只是在其实现中向您公开这些,所以它应该结转。 GCDAsyncSocket ssl_startTLS -> status = SSLSetCertificate(sslContext, certs); 中的 OSStatus 总是得到 -50(/*用户参数列表中的错误*/)。我用我使用的新代码更新我的问题【参考方案4】:

我只是想将这个提供给今天看到这个的任何人 - 我创建了一个包来帮助在 iOS 上使用新的 iOS 13 限制的 TLS。把它放在这里以防它帮助某人。随意贡献:

https://github.com/eamonwhiter73/IOSObjCWebSockets

【讨论】:

以上是关于Objective-C:评估由我们自己的 PKI(根 CA)在 TLS TCP 连接上签名的服务器证书的主要内容,如果未能解决你的问题,请参考以下文章

PKI/CA CA功能细分

PKI与证书服务

iOS开发系列—Objective-C之内存管理

iOS开发系列—Objective-C之内存管理

PKI相关知识简述

#yyds干货盘点# web安全day12:PKI