Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
451 views
in Technique[技术] by (71.8m points)

ios - SecTrustEvaluate always returns kSecTrustResultRecoverableTrustFailure with SecPolicyCreateSSL

My application tries to evaluate a server trust certificate for a self signed certificate. This is working fine with SecPolicyCreateBasicX509 but not working for SecPolicyCreateSSL

Here is my code:

if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
        // create trust from protection space
        SecTrustRef trustRef;
        int trustCertificateCount = SecTrustGetCertificateCount(challenge.protectionSpace.serverTrust);

        NSMutableArray* trustCertificates = [[NSMutableArray alloc] initWithCapacity:trustCertificateCount];
        for (int i = 0; i < trustCertificateCount; i++) {
            SecCertificateRef trustCertificate =  SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, i);
            [trustCertificates addObject:(id) trustCertificate];
        }            

        // set evaluation policy
        SecPolicyRef policyRef;
        // policyRef = SecPolicyCreateBasicX509(); this is working
        policyRef = SecPolicyCreateSSL(NO, (CFStringRef)             
        SecTrustCreateWithCertificates((CFArrayRef) trustCertificates, policyRef, &trustRef);

        [trustCertificates release];

        // load known certificates from keychain and set as anchor certificates
        NSMutableDictionary* secItemCopyCertificatesParams = [[NSMutableDictionary alloc] init];    
        [secItemCopyCertificatesParams setObject:(id)kSecClassCertificate forKey:(id)kSecClass];
        [secItemCopyCertificatesParams setObject:@"Server_Cert_Label" forKey:(id)kSecAttrLabel];
        [secItemCopyCertificatesParams setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnRef];
        [secItemCopyCertificatesParams setObject:(id)kSecMatchLimitAll forKey:(id)kSecMatchLimit];

        CFArrayRef certificates;
        certificates = nil;
        SecItemCopyMatching((CFDictionaryRef) secItemCopyCertificatesParams, (CFTypeRef*) &certificates);

        if (certificates != nil && CFGetTypeID(certificates) == CFArrayGetTypeID()) {
            SecTrustSetAnchorCertificates(trustRef, certificates);
            SecTrustSetAnchorCertificatesOnly(trustRef, NO);
        }

        SecTrustResultType result;
        OSStatus trustEvalStatus = SecTrustEvaluate(trustRef, &result);
        if (trustEvalStatus == errSecSuccess) {
            if (result == kSecTrustResultConfirm || result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) {
                // evaluation OK
                [challenge.sender useCredential:[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
            } else {
                // evaluation failed 
                // ask user to add certificate to keychain
        } else {
            // evaluation failed - cancel authentication
            [[challenge sender] cancelAuthenticationChallenge:challenge];
        }
}

After a lot of research i have already made changes to the self-signed certificate by adding extension like mentioned in this post: Unable to trust a self signed certificate on iphone

Does anyone have another hint what might be missing here?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

After a lot of testing I have worked out this problem. The following has been changed.

  • The policy is set to NO for server evaluation. This means the certificate is checked for client authentication. Obviously the server certificate will not have this! Setting this to YES will actually check if extendedKeyUsage is set to serverAuth for the server certificate.

  • SecTrustSetAnchorCertificates and SecTrustSetAnchorCertificatesOnly should always be called before evaluation and not only if you are providing your own anchor certificates. You need to call this with an empty array, otherwise the system known anchor certificates are not used for evaluation. Even installed trusted root certificates from MDM are working then.

Here is a working sample based on the first code:

if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
    // create trust from protection space
    SecTrustRef trustRef;
    int trustCertificateCount = SecTrustGetCertificateCount(challenge.protectionSpace.serverTrust);

    NSMutableArray* trustCertificates = [[NSMutableArray alloc] initWithCapacity:trustCertificateCount];
    for (int i = 0; i < trustCertificateCount; i++) {
        SecCertificateRef trustCertificate =  SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, i);
        [trustCertificates addObject:(id) trustCertificate];
    }            

    // set evaluation policy
    SecPolicyRef policyRef;
    // set to YES to verify certificate extendedKeyUsage is set to serverAuth
    policyRef = SecPolicyCreateSSL(YES, (CFStringRef) challenge.protectionSpace.host);
    SecTrustCreateWithCertificates((CFArrayRef) trustCertificates, policyRef, &trustRef);

    [trustCertificates release];

    // load known certificates from keychain and set as anchor certificates
    NSMutableDictionary* secItemCopyCertificatesParams = [[NSMutableDictionary alloc] init];    
    [secItemCopyCertificatesParams setObject:(id)kSecClassCertificate forKey:(id)kSecClass];
    [secItemCopyCertificatesParams setObject:@"Server_Cert_Label" forKey:(id)kSecAttrLabel];
    [secItemCopyCertificatesParams setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnRef];
    [secItemCopyCertificatesParams setObject:(id)kSecMatchLimitAll forKey:(id)kSecMatchLimit];

    CFArrayRef certificates;
    certificates = nil;
    SecItemCopyMatching((CFDictionaryRef) secItemCopyCertificatesParams, (CFTypeRef*) &certificates);

    if (certificates != nil && CFGetTypeID(certificates) == CFArrayGetTypeID()) {
        SecTrustSetAnchorCertificates(trustRef, certificates);
        SecTrustSetAnchorCertificatesOnly(trustRef, NO);
    } else {
        // set empty array as own anchor certificate so system anchos certificates are used too!
        SecTrustSetAnchorCertificates(trustRef, (CFArrayRef) [NSArray array]);
        SecTrustSetAnchorCertificatesOnly(trustRef, NO);
    }

    SecTrustResultType result;
    OSStatus trustEvalStatus = SecTrustEvaluate(trustRef, &result);
    if (trustEvalStatus == errSecSuccess) {
        if (result == kSecTrustResultConfirm || result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) {
            // evaluation OK
            [challenge.sender useCredential:[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
        } 
        else {
            // evaluation failed 
            // ask user to add certificate to keychain
        }
    } 
    else {
        // evaluation failed - cancel authentication
        [[challenge sender] cancelAuthenticationChallenge:challenge];
    }
}

Hope this will help someone.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...