Xcode SSL固定信任锚证书

时间:2017-04-25 17:54:20

标签: ios cocoa ssl ssl-certificate pinning

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

尝试了几个代码并且总是返回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];
}

1 个答案:

答案 0 :(得分:0)

你不应该自己这样做:API(你可以看到)非常棘手,除非你是SSL和iOS专家,否则几乎不可能得到正确的验证。

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

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