SecTrustEvaluate всегда возвращает kSecTrustResultRecoverableTrustFailure с SecPolicyCreateSSL

Мое приложение пытается оценить сертификат доверия сервера для самозаверяющего сертификата. Это работает нормально с SecPolicyCreateBasicX509, но не работает для SecPolicyCreateSSL

Вот мой код:

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

После многих исследований я уже внес изменения в самоподписанный сертификат, добавив расширение, как упомянуто в этом посте: Невозможно доверять самоподписанному сертификату на iphone.

У кого-нибудь есть еще один намек на то, что здесь может отсутствовать?

2 ответа

Решение

После многих испытаний я решил эту проблему. Следующее было изменено.

  • Политика оценивается как NO для оценки сервера. Это означает, что сертификат проверен на подлинность клиента. Очевидно, что сертификат сервера не будет иметь этого! Установка этого значения в YES фактически проверит, extendedKeyUsage установлен в serverAuth для сертификата сервера.

  • SecTrustSetAnchorCertificates а также SecTrustSetAnchorCertificatesOnly всегда следует вызывать перед оценкой, а не только если вы предоставляете свои собственные якорные сертификаты. Вы должны вызывать это с пустым массивом, иначе известные системные сертификаты привязки не используются для оценки. Даже установленные доверенные корневые сертификаты от MDM работают тогда.

Вот рабочий пример, основанный на первом коде:

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

Надеюсь, это кому-нибудь поможет.

Это может быть проблема с сертификатом сервера....

Проверьте здесь, я решил мою проблему kSecTrustResultRecoverableTrustFailure, добавив subjectAltName = DNS:example.com в конфигурационный файл openssl, в частности, при генерации ключей сервера...

Если вы не используете openssl для его генерации, извините, но я могу вам помочь. В любом случае, если вы хотите использовать openssl, вот хороший учебник, чтобы сгенерировать эти ключи и подписать их с помощью своего собственного корневого центра сертификации.

Из этого урока я просто изменил свой конфигурационный файл сервера openssl на:

    [server] basicConstraints = критический,CA:FALSE
    keyUsage = digitalSignature, keyEncipherment, dataEncipherment
    extendedKeyUsage = serverAuth
    nsCertType = имя_сервера сервера AltName = IP:10.0.1.5,DNS:office.totendev.com 

Надеюсь, поможет!

Другие вопросы по тегам