¿Cómo usar NSURLConnection para conectarse con SSL para un certificado que no es de confianza?

Tengo el siguiente código simple para conectarme a una página web SSL

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

Excepto que da un error si el Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate". ¿Hay alguna manera de configurarlo para aceptar conexiones de todos modos (al igual que en un navegador puedes presionar aceptar) o una forma de eludirlo?

¡Hay una API compatible para lograr esto! Agregue algo como esto a su delegado de 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]; } 

Tenga en cuenta que connection:didReceiveAuthenticationChallenge: puede enviar su mensaje a challenge.sender (mucho) más tarde, después de presentar un cuadro de diálogo al usuario si es necesario, etc.

Si no está dispuesto (o no puede) utilizar API privadas, hay una biblioteca de código abierto (licencia BSD) llamada ASIHTTPRequest que proporciona un envoltorio alrededor de las CFNetwork APIs nivel CFNetwork APIs . Recientemente introdujeron la capacidad de permitir HTTPS connections utilizando certificados autofirmados o no confiables con la API -setValidatesSecureCertificate: . Si no desea obtener toda la biblioteca, puede usar la fuente como referencia para implementar la misma funcionalidad usted mismo.

Idealmente, solo debería haber dos escenarios de cuándo una aplicación de iOS necesitaría aceptar un certificado no confiable.

Escenario A: está conectado a un entorno de prueba que utiliza un certificado autofirmado.

Escenario B: está Proxingando tráfico HTTPS utilizando un MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc. Los Proxies devolverán un certificado firmado por una CA autofirmada para que el proxy pueda capturar el tráfico HTTPS .

Los hosts de producción nunca deben usar certificados no confiables por razones obvias .

Si necesita que el simulador de iOS acepte un certificado no confiable para fines de prueba, se recomienda encarecidamente que no cambie la lógica de la aplicación para deshabilitar la validación de certificados incorporada provista por las API de NSURLConnection . Si la aplicación se lanza al público sin eliminar esta lógica, será susceptible a los ataques man-in-the-middle.

La forma recomendada de aceptar certificados no confiables para fines de prueba es importar el certificado de autoridad (CA) certificado que firmó el certificado en su simulador iOS o dispositivo iOS. Escribí una publicación de blog rápida que demuestra cómo hacer esto, que un simulador de iOS en:

aceptar certificados no confiables con el simulador ios

NSURLRequest tiene un método privado llamado setAllowsAnyHTTPSCertificate:forHost: que hará exactamente lo que te gustaría. Puede definir el método allowsAnyHTTPSCertificateForHost: en NSURLRequest través de una categoría, y configurarlo para que devuelva YES al host que desea anular.

Para complementar la respuesta aceptada, para una seguridad mucho mejor, puede agregar su certificado de servidor o su propio certificado de CA raíz a keychain ( https://stackoverflow.com/a/9941559/1432048 ), sin embargo, hacer esto solo no hará que NSURLConnection autentica tu servidor autofirmado automáticamente. Aún necesita agregar el código siguiente a su delegado de NSURLConnection, se copió del código de muestra de Apple AdvancedURLConnections de Apple, y necesita agregar dos archivos (Credentials.h, Credentials.m) del código de muestra de apple a sus proyectos.

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

No puedo atribuir ningún crédito por esto, pero este que encontré funcionó muy bien para mis necesidades. shouldAllowSelfSignedCert es mi variable BOOL . Solo agregue a su delegado de NSURLConnection y debería estar listo para un bypass rápido por conexión.

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

En iOS 9, las conexiones SSL fallarán para todos los certificados no válidos o autofirmados. Este es el comportamiento predeterminado de la nueva característica de seguridad de transporte de aplicaciones en iOS 9.0 o posterior, y en OS X 10.11 y posterior.

Puede anular este comportamiento en Info.plist , estableciendo NSAllowsArbitraryLoads en YES en el diccionario NSAppTransportSecurity . Sin embargo, recomiendo anular esta configuración solo para fines de prueba.

enter image description here

Para obtener información, consulte la técnica de transporte de aplicaciones aquí .

La solución temporal de categoría publicada por Nathan de Vries superará las comprobaciones API privadas de la AppStore, y es útil en los casos en que no tenga control del objeto NSUrlConnection . Un ejemplo es NSXMLParser que abrirá la URL que proporcione, pero no expone NSURLRequest o NSURLConnection .

En iOS 4 la solución aún parece funcionar, pero solo en el dispositivo, el simulador ya no invoca el método allowsAnyHTTPSCertificateForHost:

NSURLConnectionDelegate usar NSURLConnectionDelegate para permitir conexiones HTTPS y hay nuevas devoluciones de llamada con iOS8.

Obsoleto:

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

En lugar de eso, debes declarar:

connectionShouldUseCredentialStorage: – Enviado para determinar si el cargador de URL debe usar el almacenamiento de credenciales para autenticar la conexión.

connection:willSendRequestForAuthenticationChallenge: dice al delegado que la conexión enviará una solicitud para un desafío de autenticación.

Con willSendRequestForAuthenticationChallenge puede usar el challenge como lo hizo con los métodos en desuso, por ejemplo:

 // Trusting and not trusting connection to host: Self-signed certificate [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; 

Publiqué un código esencial (basado en el trabajo de otra persona que anoto) que te permite autenticarte correctamente contra un certificado autogenerado (y cómo obtener un certificado gratis – ver comentarios en la parte inferior de Cocoanetics )

Mi código está aquí github

Si desea seguir utilizando sendSynchronousRequest, trabajo en esta solución:

 FailCertificateDelegate *fcd=[[FailCertificateDelegate alloc] init]; NSURLConnection *c=[[NSURLConnection alloc] initWithRequest:request delegate:fcd startImmediately:NO]; [c setDelegateQueue:[[NSOperationQueue alloc] init]]; [c start]; NSData *d=[fcd getData]; 

puede verlo aquí: Conexión síncrona SSL Objective-C

Con AFNetworking , he consumido con éxito el servicio web https con el siguiente código,

 NSString *aStrServerUrl = WS_URL; // Initialize AFHTTPRequestOperationManager... AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; manager.requestSerializer = [AFJSONRequestSerializer serializer]; manager.responseSerializer = [AFJSONResponseSerializer serializer]; [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; manager.securityPolicy.allowInvalidCertificates = YES; [manager POST:aStrServerUrl parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { successBlock(operation, responseObject); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { errorBlock(operation, error); }]; 

Puedes usar este Código

 -(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodServerTrust) { [[challenge sender] useCredential:[NSURLCredential credentialForTrust:[[challenge protectionSpace] serverTrust]] forAuthenticationChallenge:challenge]; } } 

Use -connection:willSendRequestForAuthenticationChallenge: lugar de estos métodos obsoletos

Obsoleto:

 -(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace -(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge -(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge