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

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

Я разрабатываю приложение для 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 Code = -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

4b9b3361

Ответ 1

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

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

Ответ 2

Apple имеет Техническую ноту 2232, которая довольно информативна и подробно описывает оценку доверия HTTPS-сервера.

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

Из Технической заметки:

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

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

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

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

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

Если вы хотите разрешить подключение, создайте учетные данные из своего объект доверия (используя +[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);
    }
}

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

Ответ 3

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

Ответ 4

Сделайте себе огромную услугу и не делайте этого.

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

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

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

Ответ 5

То же самое, что и решение friherd, но быстро:

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

Ответ 6

просто нужно добавить .cer в SecTrust и передать ATS

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)
    }
}

Ответ 7

Для 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)
    }
}

Тем не менее, вы можете адаптировать принятие любого самоподписанного сертификата для вашего предоставленного домена в соответствии с очень конкретным. Убедитесь, что вы добавили этот сертификат раньше, чтобы установить его в комплект. Я назвал его здесь "cert.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
        }
    }
}

Ответ 8

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

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

Ответ 9

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

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

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

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

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

Ответ 10

обновить 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)
                }
            }

        }
    }

Ответ 11

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

- (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-соединения, но здесь не учитываются полномочия подписи, что может снизить безопасность.