ios - valido - ¿Cómo usar NSURLConnection para conectarse con SSL para un certificado no confiable?
error de certificado correo (13)
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 forma de configurarlo para que acepte conexiones de todos modos (al igual que en un navegador puede presionar aceptar) o una manera de evitarlo?
¡Hay una API soportada 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.
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);
}];
En iOS 9, las conexiones SSL fallarán para todos los certificados inválidos o autofirmados. Este es el comportamiento predeterminado de la nueva función 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
, configurando NSAllowsArbitraryLoads
en YES
en el diccionario NSAppTransportSecurity
. Sin embargo, recomiendo anular esta configuración solo con fines de prueba.
Para obtener información, consulte la aplicación de transporte Technote here .
Idealmente, solo debería haber dos situaciones en las que una aplicación de iOS deba aceptar un certificado que no sea de confianza.
Escenario A: está conectado a un entorno de prueba que utiliza un certificado autofirmado.
Escenario B: Usted está procesando el tráfico HTTPS
utilizando un MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.
Los hosts de producción nunca deben usar certificados que no sean de confianza por razones obvias .
Si necesita que el simulador de iOS acepte un certificado que no sea de confianza para realizar pruebas, le recomendamos que no cambie la lógica de la aplicación para deshabilitar la validación de certificado incorporada proporcionada por las API de NSURLConnection
. Si la aplicación se lanza al público sin eliminar esta lógica, será susceptible de ataques de intermediario.
La forma recomendada de aceptar certificados no confiables para fines de prueba es importar el certificado de la Autoridad de Certificación (CA) que firmó el certificado en su simulador de iOS o dispositivo iOS. Escribí una publicación rápida en el blog que demuestra cómo hacer esto con un simulador de iOS en:
aceptar certificados no confiables usando el simulador de ios
La solución de la categoría publicada por Nathan de Vries pasará las comprobaciones de la API privada 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 parece seguir funcionando, pero solo en el dispositivo, el simulador ya no invoca el método allowsAnyHTTPSCertificateForHost:
.
No puedo tomarme ningún crédito por esto, pero encontré que funcionó muy bien para mis necesidades. shouldAllowSelfSignedCert
es mi variable BOOL
. Simplemente agréguelo a su delegado de NSURLConnection
y debería estar rockeando 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;
}
Para complementar la respuesta aceptada, para una mejor seguridad, puede agregar su certificado de servidor o su propio certificado de CA raíz a keychain ( https://.com/a/9941559/1432048 ), sin embargo, hacer esto solo no hará que NSURLConnection autentique su servidor autofirmado automáticamente. Aún debe agregar el siguiente código a su delegado de NSURLConnection, se ha copiado del código de ejemplo de Apple AdvancedURLConnections y necesita agregar dos archivos (Credentials.h, Credentials.m) del código de ejemplo 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];
}
Publiqué un código esencial (basado en el trabajo de otra persona que anoté) que te permite autenticar correctamente contra un certificado autogenerado (y cómo obtener un certificado gratuito; consulta los comentarios de Cocoanetics )
Mi código está aquí github
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 en desuso
Obsoleto:
-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
-(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
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];
Puedes verlo aquí: Objective-C SSL Synchronous Connection
Si no está dispuesto (o no puede) a usar 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 acceder a toda la biblioteca, puede usar la fuente como una referencia para implementar la misma funcionalidad.
NSURLConnectionDelegate
utilizar NSURLConnectionDelegate
para permitir conexiones HTTPS y hay nuevas devoluciones de llamada con iOS8.
Obsoleto:
connection:canAuthenticateAgainstProtectionSpace:
connection:didCancelAuthenticationChallenge:
connection:didReceiveAuthenticationChallenge:
En lugar de eso, necesitas declarar:
connectionShouldUseCredentialStorage:
envía para determinar si el cargador de URL debe usar el almacenamiento de credenciales para autenticar la conexión.
connection:willSendRequestForAuthenticationChallenge:
- Le 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];
NSURLRequest
tiene un método privado llamado setAllowsAnyHTTPSCertificate:forHost:
que hará exactamente lo que quieras. Puede definir el método allowsAnyHTTPSCertificateForHost:
en NSURLRequest
través de una categoría, y configurarlo para que devuelva YES
para el host que desea anular.