ios - Xcode SSL 固定信任 anchor 证书

标签 ios cocoa ssl ssl-certificate pinning

我不是 iOS 和 SSL 固定专家。尝试将本地证书添加到 anchor 中以信任它们。

尝试了几个代码并总是得到一个 kSecTrustResultRecoverableTrustFailure 返回。

这段代码有什么问题? 我应该将 cer 转换为 der 吗? 我应该删除服务器证书并仅使用本地受信任的证书吗?

任何想法,输入?

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {

NSString * derCaPath;
NSMutableArray * chain = [NSMutableArray array];

for(int i=0; i<= 3; i++)
{
    if (i==0)
        derCaPath = [[NSBundle mainBundle] pathForResource:@"dstrootcax3" ofType:@"cer"];
    if (i==1)
        derCaPath = [[NSBundle mainBundle] pathForResource:@"comodorsacertificationauthority" ofType:@"cer"];
    if (i==2)
        derCaPath = [[NSBundle mainBundle] pathForResource:@"geotrustglobalca" ofType:@"cer"];
    else
        derCaPath = [[NSBundle mainBundle] pathForResource:@"thawteprimaryrootca" ofType:@"cer"];

    NSData *derCA = [NSData dataWithContentsOfFile:derCaPath];

    SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)derCA);

    //NSArray * chain = [NSArray arrayWithObject:(__bridge id)(caRef)];

    [chain addObject:(__bridge id)(caRef)];
}


caChainArrayRef = CFBridgingRetain(chain);

if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
    SecTrustRef trust = nil;
    SecTrustResultType result;
    OSStatus err = errSecSuccess;

#if DEBUG
    {
        NSLog(@"Chain received from the server (working 'up'):");
        CFIndex certificateCount = SecTrustGetCertificateCount(challenge.protectionSpace.serverTrust);
        for(int i = 0; i < certificateCount; i++) {
            SecCertificateRef certRef = SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, i);
            //CFStringRef str = SecCertificateCopyLongDescription(kCFAllocatorDefault, certRef, nil);
            //NSLog(@"   %02i: %@", 1+i, str);
            //CFRelease(str);
        }

        NSLog(@"Local Roots we trust:");
        for(int i = 0; i < CFArrayGetCount(caChainArrayRef); i++) {
            SecCertificateRef certRef = (SecCertificateRef) CFArrayGetValueAtIndex(caChainArrayRef, i);
            //CFStringRef str = SecCertificateCopyLongDescription(kCFAllocatorDefault, certRef, nil);
            //NSLog(@"   %02i: %@", 1+i, str);
            //CFRelease(str);
        }
    }
#endif

    if (checkHostname) {
        // We use the standard Policy of SSL - which also checks hostnames.
        // -- see SecPolicyCreateSSL() for details.
        //
        trust = challenge.protectionSpace.serverTrust;
        //
#if DEBUG
        NSLog(@"The certificate is expected to match '%@' as the hostname",
              challenge.protectionSpace.host);
#endif
    } else {
        // Create a new Policy - which goes easy on the hostname.
        //

        // Extract the chain of certificates provided by the server.
        //
        CFIndex certificateCount = SecTrustGetCertificateCount(challenge.protectionSpace.serverTrust);
        NSMutableArray * chain = [NSMutableArray array];

        for(int i = 0; i < certificateCount; i++) {
            SecCertificateRef certRef = SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, i);
            [chain addObject:(__bridge id)(certRef)];
        }

        for(int i = 0; i < CFArrayGetCount(caChainArrayRef); i++) {
            SecCertificateRef certRef = (SecCertificateRef) CFArrayGetValueAtIndex(caChainArrayRef, i);
            [chain addObject:(__bridge id)(certRef)];
        }


        // And create a bland policy which only checks signature paths.
        //
        if (err == errSecSuccess)
            err = SecTrustCreateWithCertificates((__bridge CFArrayRef)(chain),
                                                 SecPolicyCreateBasicX509(), &trust);
#if DEBUG
        NSLog(@"The certificate is NOT expected to match the hostname '%@' ",
              challenge.protectionSpace.host);
#endif
    };

    // Explicity specify the list of certificates we actually trust (i.e. those I have hardcoded
    // in the app - rather than those provided by some randon server on the internet).
    //
    if (err == errSecSuccess)
        err = SecTrustSetAnchorCertificates(trust,caChainArrayRef);

    // And only use above - i.e. do not check the system its global keychain or something
    // else the user may have fiddled with.
    //
    if (err == errSecSuccess)
        err = SecTrustSetAnchorCertificatesOnly(trust, YES);

    if (err == errSecSuccess)
        err = SecTrustEvaluate(trust, &result);


    if (err == errSecSuccess) {
        switch (result) {
            case kSecTrustResultProceed:
                // User gave explicit permission to trust this specific
                // root at some point (in the past).
                //
                NSLog(@"GOOD. kSecTrustResultProceed - the user explicitly trusts this CA");
                [challenge.sender useCredential:[NSURLCredential credentialForTrust:trust]
                     forAuthenticationChallenge:challenge];
                goto done;
                break;
            case kSecTrustResultUnspecified:
                // The chain is technically valid and matches up to the root
                // we provided. The user has not had any say in this though,
                // hence it is not a kSecTrustResultProceed.
                //
                NSLog(@"GOOD. kSecTrustResultUnspecified - So things are technically trusted. But the user was not involved.");
                [challenge.sender useCredential:[NSURLCredential credentialForTrust:trust]
                     forAuthenticationChallenge:challenge];
                goto done;
                break;
            case kSecTrustResultInvalid:
                NSLog(@"FAIL. kSecTrustResultInvalid");
                break;
            case kSecTrustResultDeny:
                NSLog(@"FAIL. kSecTrustResultDeny (i.e. user said no explicitly)");
                break;
            case kSecTrustResultFatalTrustFailure:
                NSLog(@"FAIL. kSecTrustResultFatalTrustFailure");
                break;
            case kSecTrustResultOtherError:
                NSLog(@"FAIL. kSecTrustResultOtherError");
                break;
            case kSecTrustResultRecoverableTrustFailure:
                NSLog(@"FAIL. kSecTrustResultRecoverableTrustFailure (i.e. user could say OK, but has not been asked this)");
                // DM 25.04.2017 we allow connection for the moment
                [challenge.sender useCredential:[NSURLCredential credentialForTrust:trust]
                     forAuthenticationChallenge:challenge];
                goto done;
                break;
            default:
                NSAssert(NO,@"Unexpected result: %d", result);
                break;
        }
        // Reject.
        [challenge.sender cancelAuthenticationChallenge:challenge];
        goto done;
    };
    //CFStringRef str =SecCopyErrorMessageString(err,NULL);
    //NSLog(@"Internal failure to validate: result %@", str);
    //CFRelease(str);

    [[challenge sender] cancelAuthenticationChallenge:challenge];

done:
    if (!checkHostname)
        CFRelease(trust);
    return;
}
// In this example we can cancel at this point - as we only do
// canAuthenticateAgainstProtectionSpace against ServerTrust.
//
// But in other situations a more gentle continue may be appropriate.
//
// [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];

NSLog(@"Not something we can handle - so we're canceling it.");
[challenge.sender cancelAuthenticationChallenge:challenge];
}

最佳答案

您不应该自己执行此操作:API(如您所见)非常棘手,并且几乎不可能获得正确的验证,除非您既是 SSL 专家又是 iOS 专家。

例如,kSecTrustResultRecoverableTrustFailure 可能意味着很多事情,例如过期的证书;您不希望您的应用程序允许这样做。主机名验证也一样:它永远不应该被禁用(即使是为了调试:使用正确的主机名很容易生成测试证书)。

我开发了一个用于 SSL 固定的库,它负责处理所有这些事情: https://github.com/datatheorem/TrustKit .您应该尝试一下,因为它是专门为您的用例设计的。

关于ios - Xcode SSL 固定信任 anchor 证书,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43617779/

相关文章:

SSL 和 MAC(消息验证代码) header

ios - 我可以从UICollectionView中“取出”或“撕下”一个单元格吗?

ios - 无法根据 objective-c 中标签的文本大小设置单元格大小

cocoa - NSArrayController:指针与整数null的比较

ssl - 域名和动态IP地址

ssl - 如何在 Netty Handler 中获取客户端证书来识别用户?

ios - 有什么方法可以从后台跟踪当前正在运行的应用程序?

ios - 根据内容以编程方式自动调整 UITableViewCell 的大小

cocoa - 避免协议(protocol)方法找不到方法

objective-c - 该对象是否符合 KVC 标准?