¿Cómo acepto un certificado SSL autofirmado utilizando NSURLSession de iOS 7 y su familia de métodos de delegado para fines de desarrollo?

Estoy desarrollando una aplicación para iPhone. Durante el desarrollo , necesito conectarme a un servidor que esté usando un certificado SSL autofirmado. Estoy bastante seguro - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler es mi oportunidad de escribir un código de excepción para permitir esto. Sin embargo, no puedo encontrar ningún recurso que me diga cómo hacer esto. Puedo ver el siguiente error en el registro:

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

Además de esto, cuando I NSLog(@"error = %@", error); desde el método delegado anterior obtengo:

Error Domain = NSURLErrorDomain Code = -1202 “El certificado para este servidor no es válido. Es posible que se conecte a un servidor que se haga pasar por” api.mydevelopmenturl.com “, lo que podría poner en riesgo su información confidencial”. UserInfo = 0x10cbdbcf0 {NSUnderlyingError = 0x112ec9730 “El certificado para este servidor no es válido. Es posible que se conecte a un servidor que pretende ser” api.mydevelopmenturl.com “, lo que podría poner en riesgo su información confidencial.”, NSErrorFailingURLStringKey = https: //api.mydevelopmenturl.com/posts , NSErrorFailingURLKey = https://api.mydevelopmenturl.com/posts , NSLocalizedRecoverySuggestion = ¿Desea conectarse al servidor de todos modos ?, NSURLErrorFailingURLPeerTrustErrorKey =, NSLocalizedDescription = El certificado para este servidor no es válido. Es posible que se conecte a un servidor que se haga pasar por “api.mydevelopmenturl.com”, que podría poner en riesgo su información confidencial.}

¿Alguna idea sobre cómo resolver este problema? Por favor, publique el código ya que he leído los documentos conceptuales y no los entiendo. Aquí hay un ejemplo de uno que está más allá de mi: https://developer.apple.com/library/content/technotes/tn2232/_index.html

Esto funciona para mí:

 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 tiene una Nota técnica 2232 que es bastante informativa y explica en detalle la evaluación de confianza del servidor HTTPS .

En este caso, el error -1202 en el dominio NSURLErrorDomain es NSURLErrorServerCertificateUntrusted , lo que significa que la evaluación de confianza del servidor ha fallado. También podría recibir una variedad de otros errores; Apéndice A: Errores comunes de evaluación de servidores confiables enumera los más comunes.

De la nota técnica:

En la mayoría de los casos, la mejor forma de resolver una falla de evaluación de confianza del servidor es reparar el servidor. Esto tiene dos ventajas: ofrece la mejor seguridad y reduce la cantidad de código que tiene que escribir. El rest de esta nota técnica describe cómo puede diagnosticar fallas de evaluación de confianza del servidor y, si no es posible reparar el servidor, cómo puede personalizar la evaluación de confianza del servidor para permitir que su conexión continúe sin socavar por completo la seguridad del usuario.

El bit específico que está relacionado con esta pregunta es la sección sobre evaluación de confianza del servidor NSURLSession :

NSURLSession permite personalizar la evaluación de confianza del servidor HTTPS implementando el -URLSession:didReceiveChallenge:completionHandler: delegate. Para personalizar la evaluación de confianza del servidor HTTPS, busque un desafío cuyo espacio de protección tenga un método de autenticación de NSURLAuthenticationMethodServerTrust . Para esos desafíos, resuélvalos como se describe a continuación. Para otros desafíos, los que no le interesan, llame al bloque de controlador de finalización con la disposición NSURLSessionAuthChallengePerformDefaultHandling y una credencial NULL.

Cuando se trata del desafío de autenticación NSURLAuthenticationMethodServerTrust, puede obtener el objeto de confianza del espacio de protección del desafío llamando al método -serverTrust. Después de usar el objeto de confianza para realizar su propia evaluación de confianza del servidor HTTPS personalizado, debe resolver el desafío de una de estas dos maneras:

Si desea denegar la conexión, llame al bloque del controlador de finalización con la disposición NSURLSessionAuthChallengeCancelAuthenticationChallenge y una credencial NULL.

Si desea permitir la conexión, cree una credencial desde su objeto de confianza (usando +[NSURLCredential credentialForTrust:] ) y llame al bloque de manejador de finalización con esa credencial y la disposición NSURLSessionAuthChallengeUseCredential .

El resultado de todo esto es que si implementa el siguiente método delegado, puede anular la confianza del servidor para un servidor en particular:

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

Tenga en cuenta que debe manejar tanto el caso del host que coincida con el que desea anular como todos los demás casos. Si no maneja la parte “Todos los demás casos”, el resultado del comportamiento no está definido.

Encuentre en línea una autoridad confiable de certificados SSL que ofrezca una prueba gratuita de 90 días para nuevos certificados. Instala el certificado en tu servidor. Ahora tiene 90 días para desarrollar su aplicación hasta el punto en que puede decidir si vale la pena pagar o no para “renovar” el certificado. Esta es la mejor respuesta para mí, ya que mi decisión de utilizar el certificado autofirmado fue motivada económicamente y 90 días me da tiempo suficiente para desarrollar mi aplicación hasta el punto de poder decidir si vale la pena gastar dinero en un certificado SSL o no. . Este enfoque evita tener que lidiar con las implicaciones de seguridad de ejecutar una base de código ajustada para aceptar certificados autofirmados. ¡Dulce! ¡Yay para arrancar!

Hazte un gran favor y no lo hagas.

Comience leyendo el documento El código más peligroso del mundo: validación de certificados SSL en software que no es de navegador , especialmente la sección 10, “Romper o deshabilitar la validación de certificados”. Específicamente menciona un blog relacionado con Cocoa que describe específicamente cómo hacer lo que usted pide.

Pero no lo hagas Desactivar la verificación de certificados SSL es como introducir una bomba de tiempo en tu aplicación. En algún momento, algún día, se dejará habilitado accidentalmente, y una construcción entrará en la naturaleza. Y ese día, sus usuarios correrán serios riesgos.

En su lugar, debe usar un certificado firmado con un certificado intermedio que pueda instalar y confiar en ese dispositivo específico, que permitirá que la validación SSL tenga éxito sin poner en peligro ningún otro dispositivo que no sea el suyo (y solo entonces, temporalmente).

Lo mismo que la solución de friherd, pero en forma rápida:

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

Para Swift 3.0 / 4

Si solo desea permitir cualquier tipo de certificado autofirmado, puede usar el siguiente enfoque para implementar un URLSessionDelegate. Apple proporciona información adicional sobre cómo usar el URLSessionDelegate para todo tipo de métodos de autenticación: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/AuthenticationChallenges.html

Al principio, implemente el método de delegado y asigne un delegado correspondiente:

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

Ahora implementa el método del delegado 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) } } 

Aún así, puede adaptar la aceptación de cualquier certificado autofirmado para su dominio proporcionado para que coincida con uno muy específico. Asegúrese de haber agregado este certificado antes a su paquete de objectives de comstackción. Lo llamé aquí “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 } } } 

solo necesita agregar .cer a SecTrust y pasar 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) } } 

Esto funciona bien para que yo pase el auto-firmado:

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

Quizás una mejor manera es brindar al usuario la oportunidad de aceptar el certificado confirmando (visualmente) que la URL es precisa para el servicio al que se accede. Por ejemplo, si el host se ingresa en alguna configuración de aplicación, pruebe en la entrada del usuario y deje que el usuario decida allí mismo.

Considere que esta táctica de “confirmación del usuario” es utilizada por Safari, por lo tanto, tolerada por Apple, tendría sentido que se emplee lógicamente para otras aplicaciones.

Sugerir cavar en NSErrorRecoveryAttempting (no estoy haciendo nada) http://apple.co/22Au1GR

Obtenga el host confirmado, luego tome la ruta de exclusión de URL individual mencionada aquí. Dependiendo de la implementación, también puede tener sentido almacenar el host como una exclusión para referencia futura.

Esto parece algo que Apple habría implementado por naturaleza en Cocoa, pero hasta ahora, no he encontrado un ‘botón fácil’. Me hubiera gustado un indicador “kLetUserDecide” en algo en NSURL o NSURLSession en lugar de que todos tengan que implementar el método de delegado, así como el protocolo NSErrorRecoveryAttempting.

actualizar 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() 

la tarea de delegado

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

Aquí está la solución que funcionó para mí. Debe aceptar la conexión a través del delegado de la conexión, que incluye ambos mensajes:

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

Tenga en cuenta que al hacer esto, no está verificando la confiabilidad del certificado, por lo que solo el cifrado SSL de la conexión HTTPS es interesante, pero la autoridad de firma no está teniendo en cuenta aquí, lo que puede disminuir la seguridad.