Как я могу принять самозаверяющий сертификат SSL с использованием NSURLSession iOS 7 и его семейства методов делегирования для целей разработки?



Я разрабатываю приложение для iPhone. во время разработки, мне нужно подключиться к серверу, который использует самозаверяющий сертификат SSL. Я почти уверен - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler - Это моя возможность написать код исключения позволяют это сделать. Однако я не могу найти никаких ресурсов, которые говорят мне, как это сделать. Я вижу следующую ошибку в логе:



NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)


В дополнение к этому, когда я NSLog(@"error = %@", error); из приведенного выше метода делегата я получаю:




ошибка Домен=Nsurlerrordomain код=-1202 " сертификат для
этот сервер является недопустимым. Возможно, вы подключаетесь к серверу, который
притворяясь ... "api.mydevelopmenturl.com - который мог бы поставить ваш
конфиденциальная информация под угрозой."UserInfo=0x10cbdbcf0
{NSUnderlyingError=0x112ec9730 " сертификат для этого сервера
недействительный. Возможно, вы подключаетесь к серверу, который притворяется
"api.mydevelopmenturl.com" что может поставить вашу конфиденциальную информацию
с риском.", NSErrorFailingURLStringKey=https://api.mydevelopmenturl.com/posts,
NSErrorFailingURLKey=https://api.mydevelopmenturl.com/posts,
NSLocalizedRecoverySuggestion=вы хотите подключиться к
сервер в любом случае?, NSURLErrorFailingURLPeerTrustErrorkey=,
Nslocalizeddescription=сертификат для этого сервера недействителен.
Возможно, вы подключаетесь к серверу, который притворяется
"api.mydevelopmenturl.com-который мог бы ... положите вашу конфиденциальную
информация под угрозой.}




есть идеи, как решить эту проблему? Пожалуйста, напишите код, поскольку я прочитал концептуальные документы, и я их не понимаю. Вот пример того, что находится за пределами меня:https://developer.apple.com/library/content/technotes/tn2232/_index.html

912   11  

11 ответов:

это работает для меня:

NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:Nil];
...
...
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{
  if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
    if([challenge.protectionSpace.host isEqualToString:@"mydomain.com"]){
      NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
      completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
    }
  }
}

у Apple есть Техническое Примечание 2232 что довольно информативно и подробно объясняет HTTPS server trust evaluation.

в этом случае ошибка -1202 в NSURLErrorDomain домен NSURLErrorServerCertificateUntrusted, что означает, что оценка доверия сервера не удалась. Вы также можете получить множество других ошибок; Приложение A: Общие Ошибки Оценки Доверия Сервера перечислены наиболее распространенные из них.

С технической Примечание:

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

конкретный бит, который имеет отношение к этому вопросу, - это раздел оценка доверия сервера NSURLSession:

NSURLSession позволяет настроить оценку доверия сервера HTTPS с помощью реализация -URLSession:didReceiveChallenge:completionHandler: метод делегата. Чтобы настроить оценку доверия к серверу HTTPS, найдите проблема защиты помещения имеет способ аутентификации NSURLAuthenticationMethodServerTrust. Для решения этих проблем, решить их, как описано ниже. Для других проблем, которые вы не волнует, вызывает обработчик завершения блока NSURLSessionAuthChallengePerformDefaultHandling диспозиция и нуль мандатный.

при работе с NSURLAuthenticationMethodServerTrust задача проверки подлинности, вы можете получить объект доверия от вызовите пространство защиты, вызвав метод-serverTrust. После используя целевого объекта, чтобы сделать свой собственный сервер https доверять оценка, вы должны разрешить проблема в одном из двух способов:

если вы хотите запретить соединение, вызовите блок обработчика завершения с помощью элемент NSURLSessionAuthChallengeCancelAuthenticationChallenge планировка и нулевые учетные данные.

если вы хотите разрешить подключение, создайте учетные данные от вашего объект доверия (с помощью +[NSURLCredential credentialForTrust:]) и вызовите блок обработчика завершения с этими учетными данными и NSURLSessionAuthChallengeUseCredential расположение.

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

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
    if([challenge.protectionSpace.authenticationMethod
                           isEqualToString:NSURLAuthenticationMethodServerTrust])
    {
        if([challenge.protectionSpace.host
                           isEqualToString:@"domaintoverride.com"])
        {
            NSURLCredential *credential = 
                          [NSURLCredential credentialForTrust:
                                          challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
        }
        else
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    }
}

обратите внимание, что вы должны обрабатывать и случай хоста, соответствующего тому, который вы хотите переопределить и все остальные случаи. Если вы не обрабатываете часть "все остальные случаи", результат поведения не определен.

найдите надежный центр сертификации SSL в интернете, который предлагает бесплатную 90-дневную пробную версию для новых сертификатов. Установите сертификат на свой сервер. Теперь у вас есть 90 дней, чтобы разработать свое приложение до такой степени, что вы можете принять решение о том, стоит ли платить деньги за "продление" сертификата. Это лучший ответ для меня, так как мое решение использовать самоподписанный сертификат было финансово мотивировано, и 90 дней дает мне достаточно времени для разработки моего приложения до такой степени, что я могу решите, стоит ли тратить деньги на сертификат SSL или нет. Такой подход позволяет избежать необходимости иметь дело с последствиями для безопасности выполнения кода, который оптимизирован для бизнеса. Мило! Ура для бутстрэппинга!

у себя огромный пользу и не надо.

начните с чтения статьи самое опасное код в мире: проверка SSL-сертификатов в браузере, особенно раздел 10,"нарушение или отключение проверки сертификата". Он специально вызывает блог, связанный с какао, который специально описывает, как делать то, что вы просите.

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

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

то же, что и решение friherd, но в swift:

func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust{
        let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)
        completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential,credential);
    }
}

Для Swift 3.0 / 4

если вы просто хотите разрешить любые самозаверяющие сертификаты, вы можете использовать следующий подход для реализации URLSessionDelegate. Apple предоставляет дополнительную информацию о том, как использовать URLSessionDelegate для всех видов методов аутентификации: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/AuthenticationChallenges.html

сначала реализовать делегировать метод и назначить соответствующий делегат:

let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
let task = urlSession.dataTask(with: urlRequest).resume()

теперь реализуем метод делегата https://developer.apple.com/documentation/foundation/nsurlsessiondelegate/1409308-urlsession?language=objc

func urlSession(_ session: URLSession, 
     didReceive challenge: URLAuthenticationChallenge, 
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

    guard challenge.previousFailureCount == 0 else {
        challenge.sender?.cancel(challenge)
        // Inform the user that the user name and password are incorrect
        completionHandler(.cancelAuthenticationChallenge, nil)
        return
    }

    // Within your authentication handler delegate method, you should check to see if the challenge protection space has an authentication type of NSURLAuthenticationMethodServerTrust
    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
       // and if so, obtain the serverTrust information from that protection space.
       && challenge.protectionSpace.serverTrust != nil
       && challenge.protectionSpace.host == "yourdomain.com" {
        let proposedCredential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
        completionHandler(URLSession.AuthChallengeDisposition.useCredential, proposedCredential)
    }
}

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

func urlSession(_ session: URLSession, 
     didReceive challenge: URLAuthenticationChallenge, 
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

    guard challenge.previousFailureCount == 0 else {
        challenge.sender?.cancel(challenge)
        // Inform the user that the user name and password are incorrect
        completionHandler(.cancelAuthenticationChallenge, nil)
        return
    }

    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
       && challenge.protectionSpace.serverTrust != nil
       && challenge.protectionSpace.host == "yourdomain.com" {

        if let trust = challenge.protectionSpace.serverTrust,
           let pem = Bundle.main.url(forResource:"cert", withExtension: "cer"),
           let data = NSData(contentsOf: pem),
           let cert = SecCertificateCreateWithData(nil, data) {
            let certs = [cert]
            SecTrustSetAnchorCertificates(trust, certs as CFArray)

            let proposedCredential = URLCredential(trust: trust)
                completionHandler(URLSession.AuthChallengeDisposition.useCredential, proposedCredential)
            return
        }
    }
}

просто нужно добавить .cer к сектанту и его передают на АТС

class NSURLSessionPinningDelegate: NSObject, URLSessionDelegate {

    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {

        if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
            if let trust = challenge.protectionSpace.serverTrust,
               let pem = Bundle.main.path(forResource: "https", ofType: "cer"),
               let data = NSData(contentsOfFile: pem),
               let cert = SecCertificateCreateWithData(nil, data) {
                let certs = [cert]
                SecTrustSetAnchorCertificates(trust, certs as CFArray)

                completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: trust))
                return
            }
        }

        // Pinning failed
        completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
    }
}

Это прекрасно работает для меня, чтобы пройти самоподписанный :

Delegate : NSURLSessionDelegate

- (void)URLSession:(NSURLSession *)session **task**:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}

возможно, лучший способ-предоставить пользователю возможность принять сертификат, подтверждающий (визуально), что URL-адрес точен для доступа к сервису. Например, если хост введен в какой-либо параметр приложения, проверьте запись пользователя и позвольте пользователю решить прямо там.

считайте, что эта тактика" подтверждения пользователя " используется Safari, таким образом, оправданная Apple, было бы разумно, что она будет использоваться логически для других приложений.

предлагаю копаться в NSErrorRecoveryAttempting (я не делаю сам) http://apple.co/22Au1GR

получить подтверждение хоста, а затем взять индивидуальный маршрут исключения URL, упомянутый здесь. В зависимости от реализации может также иметь смысл хранить хост в качестве исключения для дальнейшего использования.

Это похоже на то, что Apple реализовала бы по своей природе в какао, но пока я не нашел "легкую кнопку". Бы вам понравился флаг "kLetUserDecide" на чем-то в NSURL или NSURLSession вместо того, чтобы каждый должен был реализовать метод делегата, а также протокол NSErrorRecoveryAttempting.

обновить xcode 9

    var result:(message:String, data:Data?) = (message: "Fail", data: nil)
    var request = URLRequest(url: url)

    let sessionDelegate = SessionDelegate()
    let session = URLSession(configuration: .default, delegate: sessionDelegate, delegateQueue: nil)
    let task = session.dataTask(with: request){(data, response, error) in


    }
    task.resume()

задача делегата

    class SessionDelegate:NSObject, URLSessionDelegate
    {

        func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
            if(challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust)
            {
                print(challenge.protectionSpace.host) 
                if(challenge.protectionSpace.host == "111.11.11.11")
                {
                    let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
                   completionHandler(URLSession.AuthChallengeDisposition.useCredential, credential)
                }
            }

        }
    }

вот решение, которое сработало для меня. Вам нужно принять соединение через делегат соединения, включая оба сообщения:

- (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])
        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];

    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}

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

Comments

    Ничего не найдено.