Закрепление SSL с помощью AFNetworking не работает

Я пытаюсь добавить закрепление SSL в свое приложение с самозаверяющим сертификатом, но не могу заставить его работать. Я пробовал все, что мог найти в Интернете, но безуспешно, и отсутствие эксперта в том, как работает SSL, не помогает.

Я использую Objective-C с последней версией AFNetworking.

Я сделал очень простой фрагмент кода для проверки своих вызовов API (я использую URL-адрес заполнителя для этого поста):

NSString *url = @"https://api.example.net/webservice";

    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"example.net" ofType:@"der"];
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];

    AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:url]];
    manager.requestSerializer = [AFJSONRequestSerializer new];
    manager.responseSerializer = [AFJSONResponseSerializer new];

    AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    [policy setAllowInvalidCertificates:YES];
    [policy setValidatesDomainName:NO];
    policy.pinnedCertificates = [NSSet setWithObject:certData];

    manager.securityPolicy = policy;

    [manager POST:url parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"SUCCESS");
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"FAILURE : %@", error.localizedDescription);
    }];

Каждый раз, когда я пытаюсь выполнить этот код, я получаю ошибку со следующей ошибкой:

Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “api.example.net” which could put your confidential information at risk."

Я пытался использовать разные форматы для своего сертификата (.der, .cer, ...), но все равно всегда получаю ту же ошибку.

Я пытался использовать NSAllowsArbitraryLoads в своем info.plist, но ничего не изменилось.

Чтобы убедиться, что я использую рабочий код, я также загрузил пример проекта из учебник Рэя Вендерлиха, но мой собственный сертификат по-прежнему недействителен (в учебнике используется сертификат stackexchange, этот работает).

Я исследовал эту проблему в течение нескольких дней и еще не нашел решения.

Этот же сертификат отлично работает в нашем приложении для Android, а также в Postman.

Это потому, что я использую самозаверяющий сертификат, а iOS это не нравится? Есть ли что-то очевидное, что я пропустил в своем коде или в конфигурации моего приложения? Есть ли что-то конкретное для реализации на стороне сервера, чтобы убедиться, что оно работает с iOS? Должен ли я экспортировать свой сертификат в особом формате?

Любая информация приветствуется.

Спасибо!


person Clément Casu    schedule 13.11.2020    source источник


Ответы (1)


Я смотрю на старый проект, в котором я без проблем использовал самозаверяющие сертификаты. Это просто комментарии, которые могут помочь — я делаю их здесь, потому что у меня больше места и я могу лучше отформатировать их.

Версия der работала.

В Info.plist вам нужно что-то вроде следующего.

    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>server1.local</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
            <key>server2.local</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
            <key>server3.local</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
        </dict>
    </dict>

Обратите внимание на ваше сообщение об ошибке — оно жалуется на сертификат сервера, так что, возможно, проблема в DNS или сертификате сервера. Оба должны быть правильными, и имя, которое вы используете, например. server1.local должно соответствовать DNS-имени сервера, а также CN сертификата для работы iOS.

Я добавил оба сертификата ЦС и сертификата сервера в цепочку в iOS.

Я верю, что это поможет вам.

FWIW моя реализация не использовала AFNetworking, но я использовал SecTrustSetAnchorCertificates внутри URLSession:didReceiveChallenge:completionHandler: в NSURLSessionDelegate, который я использовал для поддержки обычного NSURLRequest.

Вот этот кусок кода.

// Look to see if we can handle the challenge
- ( void ) URLSession:( NSURLSession                 * ) session
  didReceiveChallenge:( NSURLAuthenticationChallenge * ) challenge
    completionHandler:( void ( ^ ) ( NSURLSessionAuthChallengeDisposition, NSURLCredential * ) ) completionHandler
{
#ifdef DEBUG
    NSLog( @"didReceiveChallenge %@ %zd", challenge.protectionSpace.authenticationMethod, ( ssize_t ) challenge.previousFailureCount );
#endif
    NSURLCredential      * credential = nil;
    NSURLProtectionSpace * protectionSpace;
    SecTrustRef            trust;
    int                    err;

    // Setup
    protectionSpace = challenge.protectionSpace;
    trust = protectionSpace.serverTrust;
    credential = [NSURLCredential credentialForTrust:trust];

    if ( protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust )
    {
        // Build up the trust anchor using server certificates
        err = SecTrustSetAnchorCertificates ( trust, ( CFArrayRef ) fhWebSupportDelegate.serverCertificates );
        SecTrustResultType trustResult = 0;

        if ( err == noErr )
        {
            SecTrustSetAnchorCertificatesOnly ( trust, true );
            err = SecTrustEvaluate ( trust, & trustResult );
#ifdef DEBUG
            NSLog ( @"Trust result %lu", ( unsigned long ) trustResult );
#endif
        }

        BOOL trusted =
        ( err == noErr ) &&
        ( ( trustResult == kSecTrustResultProceed ) || ( trustResult == kSecTrustResultUnspecified ) || ( trustResult == kSecTrustResultRecoverableTrustFailure ) );


        // Return based on whether we decided to trust or not
        if ( trusted )
        {
#ifdef DEBUG
            NSLog ( @"Trust evaluation succeeded" );
#endif
            if ( completionHandler )
            {
                completionHandler ( NSURLSessionAuthChallengeUseCredential, credential );
            }
        }
        else
        {
#ifdef DEBUG
            NSLog ( @"Trust evaluation failed" );
#endif
            if ( completionHandler )
            {
                completionHandler ( NSURLSessionAuthChallengeCancelAuthenticationChallenge, credential );
            }
        }
    }
    else if ( completionHandler )
    {
        completionHandler ( NSURLSessionAuthChallengePerformDefaultHandling, nil );
    }
}

Здесь fhWebSupportDelegate.serverCertificates возвращает массив с ЦС, а также сертификат сервера. Также я был чрезвычайно снисходителен, когда доверял серверу, как видно из кода.

person skaak    schedule 17.11.2020
comment
Большое спасибо! Я посмотрю на это и посмотрю, удастся ли мне заставить его работать - person Clément Casu; 19.11.2020