Как использовать NSURLConnection для соединения с SSL для ненадежного сертификата? - PullRequest
297 голосов
/ 01 июня 2009

У меня есть следующий простой код для подключения к веб-странице SSL

NSMutableURLRequest *urlRequest=[NSMutableURLRequest requestWithURL:url];
[ NSURLConnection sendSynchronousRequest: urlRequest returningResponse: nil error: &error ];

За исключением того, что выдает ошибку, если сертификат является самозаверяющим Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate". Есть ли способ настроить его на прием соединений в любом случае (как в браузере, который вы можете нажать принять), или способ обойти его? *

Ответы [ 13 ]

414 голосов
/ 09 января 2010

Для этого существует поддерживаемый API! Добавьте что-то подобное своему делегату NSURLConnection:

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
  return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
  if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
    if ([trustedHosts containsObject:challenge.protectionSpace.host])
      [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];

  [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}

Обратите внимание, что connection:didReceiveAuthenticationChallenge: может отправить свое сообщение на challenge.sender (намного) позже, после представления диалогового окна пользователю при необходимости и т. Д.

36 голосов
/ 10 июня 2009

Если вы не хотите (или не можете) использовать частные API, есть библиотека с открытым исходным кодом (лицензия BSD) под названием ASIHTTPRequest , которая предоставляет оболочку для нижнего уровня CFNetwork APIs. Недавно они представили возможность разрешать HTTPS connections использование самозаверяющих или ненадежных сертификатов с -setValidatesSecureCertificate: API. Если вы не хотите извлекать всю библиотеку, вы можете использовать источник в качестве справочного материала для реализации той же функциональности самостоятельно.

33 голосов
/ 11 августа 2011

В идеале должно быть только два сценария, когда приложению iOS потребуется принять ненадежный сертификат.

Сценарий A: вы подключены к тестовой среде, в которой используется самозаверяющий сертификат.

Сценарий B: Вы проксируете трафик HTTPS, используя MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc. Прокси вернут сертификат, подписанный самозаверяющим CA, чтобы прокси мог захватывать HTTPS трафик.

Производственные хосты никогда не должны использовать недоверенные сертификаты по очевидным причинам .

Если вам нужно, чтобы симулятор iOS принял недоверенный сертификат для тестирования, настоятельно рекомендуется не изменять логику приложения, чтобы отключить встроенную проверку сертификата, предоставляемую API-интерфейсами NSURLConnection. Если приложение будет выпущено для публики без удаления этой логики, оно будет подвержено атакам «человек посередине».

Рекомендуемый способ принять недоверенные сертификаты в целях тестирования - импортировать сертификат Центра сертификации (ЦС), который подписал сертификат, на симуляторе iOS или устройстве iOS. Я написал небольшую заметку в блоге, в которой демонстрируется, как это сделать с помощью симулятора iOS:

прием ненадежных сертификатов с помощью симулятора ios

12 голосов
/ 01 июня 2009

NSURLRequest имеет закрытый метод setAllowsAnyHTTPSCertificate:forHost:, который будет делать именно то, что вы хотите. Вы можете определить метод allowsAnyHTTPSCertificateForHost: для NSURLRequest через категорию и настроить его на возврат YES для хоста, который вы хотите переопределить.

11 голосов
/ 07 февраля 2014

Чтобы дополнить принятый ответ, для большей безопасности вы можете добавить свой сертификат сервера или свой собственный сертификат корневого центра сертификации в цепочку ключей (https://stackoverflow.com/a/9941559/1432048),, однако, если сделать это самостоятельно, NSURLConnection не сможет аутентифицировать самозаверяющий сервер Вы все равно должны добавить приведенный ниже код в свой делегат NSURLConnection, он скопирован из образца кода Apple AdvancedURLConnections и вам нужно добавить два файла (Credentials.h, Credentials.m) из примера кода Apple ваши проекты.

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
//        if ([trustedHosts containsObject:challenge.protectionSpace.host])

    OSStatus                err;
    NSURLProtectionSpace *  protectionSpace;
    SecTrustRef             trust;
    SecTrustResultType      trustResult;
    BOOL                    trusted;

    protectionSpace = [challenge protectionSpace];
    assert(protectionSpace != nil);

    trust = [protectionSpace serverTrust];
    assert(trust != NULL);
    err = SecTrustEvaluate(trust, &trustResult);
    trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));

    // If that fails, apply our certificates as anchors and see if that helps.
    //
    // It's perfectly acceptable to apply all of our certificates to the SecTrust
    // object, and let the SecTrust object sort out the mess.  Of course, this assumes
    // that the user trusts all certificates equally in all situations, which is implicit
    // in our user interface; you could provide a more sophisticated user interface
    // to allow the user to trust certain certificates for certain sites and so on).

    if ( ! trusted ) {
        err = SecTrustSetAnchorCertificates(trust, (CFArrayRef) [Credentials sharedCredentials].certificates);
        if (err == noErr) {
            err = SecTrustEvaluate(trust, &trustResult);
        }
        trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
    }
    if(trusted)
        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}

[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
10 голосов
/ 24 августа 2015

В iOS 9 SSL-соединения не будут работать для всех недействительных или самозаверяющих сертификатов. Это поведение по умолчанию для новой функции App Transport Security в iOS 9.0 или более поздней версии и в OS X 10.11 и более поздней версии.

Вы можете переопределить это поведение в Info.plist, установив NSAllowsArbitraryLoads в YES в словаре NSAppTransportSecurity. Однако я рекомендую переопределить этот параметр только для целей тестирования.

enter image description here

Для получения информации см. App Transport Technote здесь .

10 голосов
/ 02 февраля 2010

Я не могу взять кредит на это, , но этот, который я нашел , работал очень хорошо для моих нужд. shouldAllowSelfSignedCert - это моя BOOL переменная. Просто добавьте к своему NSURLConnection делегату, и вы должны быть готовы к быстрому обходу для каждого отдельного соединения.

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)space {
     if([[space authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) {
          if(shouldAllowSelfSignedCert) {
               return YES; // Self-signed cert will be accepted
          } else {
               return NO;  // Self-signed cert will be rejected
          }
          // Note: it doesn't seem to matter what you return for a proper SSL cert
          //       only self-signed certs
     }
     // If no other authentication is required, return NO for everything else
     // Otherwise maybe YES for NSURLAuthenticationMethodDefault and etc.
     return NO;
}
6 голосов
/ 13 февраля 2015

Вы должны использовать NSURLConnectionDelegate, чтобы разрешить HTTPS-соединения, и есть новые обратные вызовы с iOS8.

Запрещено:

connection:canAuthenticateAgainstProtectionSpace:
connection:didCancelAuthenticationChallenge:
connection:didReceiveAuthenticationChallenge:

Вместо тех, вам нужно объявить:

connectionShouldUseCredentialStorage: - Отправляется, чтобы определить, должен ли загрузчик URL использовать хранилище учетных данных для проверки подлинности соединения.

connection:willSendRequestForAuthenticationChallenge: - Сообщает делегату, что соединение отправит запрос на вызов аутентификации.

С willSendRequestForAuthenticationChallenge вы можете использовать challenge, как вы использовали с устаревшими методами, например:

// Trusting and not trusting connection to host: Self-signed certificate
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
6 голосов
/ 21 июля 2010

Временное решение для категории, опубликованное Натаном де Врисом, пройдет частные проверки API AppStore и полезно в тех случаях, когда у вас нет контроля над объектом NSUrlConnection. Одним из примеров является NSXMLParser, который откроет указанный вами URL, но не отображает NSURLRequest или NSURLConnection.

В iOS 4 обходной путь все еще работает, но только на устройстве Simulator больше не вызывает метод allowsAnyHTTPSCertificateForHost:.

3 голосов
/ 17 марта 2012

Я опубликовал некоторый гист-код (основанный на чьей-либо работе, который я отмечаю), который позволяет вам правильно проходить аутентификацию на основе сгенерированного сертификата (и как получить бесплатный сертификат - см. Комментарии внизу Кокоанетика )

Мой код здесь github

...