Как я могу принять самозаверяющий сертификат 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
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