línea libre instalar gratuito gratis generar generador crear con como certificados certificado autofirmado ios ssl afnetworking

ios - libre - generar certificado crt



Problemas de red AF con la verificación TLS de una CA raíz del servidor autofirmado (2)

Con la ayuda de varios recursos SSL diferentes, he encontrado la solución para permitir el uso de certificados autofirmados para validar un servidor privado habilitado para SSL. También he comprendido mucho mejor el SSL, las soluciones existentes de iOS y los problemas menores de cada uno que hicieron que no funcionara en mi sistema. Intentaré resumir todos los recursos que entraron en mi solución y qué cosas pequeñas hicieron la diferencia.

Seguimos utilizando AFNetworking y actualmente es 2.6.0 que supuestamente incluye la fijación de certificados. Esta fue la raíz de nuestro problema; no pudimos verificar la identidad de nuestro servidor privado, que estaba enviando un certificado de hoja firmado por una raíz CA autofirmada. En nuestra aplicación para iOS, incluimos el certificado raíz autofirmado, que luego se establece como un anclaje de confianza mediante AFNetworking. Sin embargo, como el servidor es un servidor local (hardware incluido con nuestro producto), la dirección IP es dinámica, por lo que la validación del certificado de AFNetworking falla porque no pudimos deshabilitar la verificación de IP.

Para llegar a la raíz de la respuesta, estamos usando un AFHTTPSessionManager para implementar una sesión personalizadaDidReceiveAuthenticationChallengeCallback. (Ver: https://gist.github.com/r00m/e450b8b391a4bf312966 ) En esa devolución de llamada, validamos el certificado del servidor usando una SecPolicy que no verifica el nombre del host; vea http://blog.roderickmann.org/2013/05/validating-a-self-signed-ssl-certificate-in-ios-and-os-x-against-a-changing-host-name/ , que es una implementación anterior para NSURLConnection en lugar de NSURLSession.

El código:

Creando un AFHTTPSessionManager

var manager: AFHTTPSessionManager = AFHTTPSessionManager() manager.requestSerializer = AFJSONRequestSerializer() // force serializer to use JSON encoding manager.setSessionDidReceiveAuthenticationChallengeBlock { (session, challenge, credential) -> NSURLSessionAuthChallengeDisposition in if self.shouldTrustProtectionSpace(challenge, credential: credential) { // shouldTrustProtectionSpace will evaluate the challenge using bundled certificates, and set a value into credential if it succeeds return NSURLSessionAuthChallengeDisposition.UseCredential } return NSURLSessionAuthChallengeDisposition.PerformDefaultHandling }

Implementación de validación personalizada.

class func shouldTrustProtectionSpace(challenge: NSURLAuthenticationChallenge, var credential: AutoreleasingUnsafeMutablePointer<NSURLCredential?>) -> Bool { // note: credential is a reference; any created credential should be sent back using credential.memory let protectionSpace: NSURLProtectionSpace = challenge.protectionSpace var trust: SecTrustRef = protectionSpace.serverTrust! // load the root CA bundled with the app let certPath: String? = NSBundle.mainBundle().pathForResource("rootCA", ofType: "cer") if certPath == nil { println("Certificate does not exist!") return false } let certData: NSData = NSData(contentsOfFile: certPath!)! let cert: SecCertificateRef? = SecCertificateCreateWithData(kCFAllocatorDefault, certData).takeUnretainedValue() if cert == nil { println("Certificate data could not be loaded. DER format?") return false } // create a policy that ignores hostname let domain: CFString? = nil let policy:SecPolicy = SecPolicyCreateSSL(1, domain).takeRetainedValue() // takes all certificates from existing trust let numCerts = SecTrustGetCertificateCount(trust) var certs: [SecCertificateRef] = [SecCertificateRef]() for var i = 0; i < numCerts; i++ { let c: SecCertificateRef? = SecTrustGetCertificateAtIndex(trust, i).takeUnretainedValue() certs.append(c!) } // and adds them to the new policy var newTrust: Unmanaged<SecTrust>? = nil var err: OSStatus = SecTrustCreateWithCertificates(certs, policy, &newTrust) if err != noErr { println("Could not create trust") } trust = newTrust!.takeUnretainedValue() // replace old trust // set root cert let rootCerts: [AnyObject] = [cert!] err = SecTrustSetAnchorCertificates(trust, rootCerts) // evaluate the certificate and product a trustResult var trustResult: SecTrustResultType = SecTrustResultType() SecTrustEvaluate(trust, &trustResult) if Int(trustResult) == Int(kSecTrustResultProceed) || Int(trustResult) == Int(kSecTrustResultUnspecified) { // create the credential to be used credential.memory = NSURLCredential(trust: trust) return true } return false }

Algunas cosas que aprendí sobre swift mientras revisaba este código.

  1. La implementación de AFNetworking de setSessionDidReceiveAuthenticationChallengeBlock tiene esta firma:

    • (void) setSessionDidReceiveAuthenticationChallengeBlock: (nullable NSURLSessionAuthChallengeDisposition (^) (NSURLSession * session, NSURLAuthenticationChallenge * challenge, NSURLCredential * __nullable __autoreleasing * __nullable credential)) bloque;

El parámetro de credencial es una variable de referencia / entrada que debe asignarse. En swift se ve así: AutoreleasingUnsafeMutablePointer. Para asignarle algo en C, harías algo como esto:

*credential = [[NSURLCredential alloc] initWithTrust...];

En swift, se ve así: (de convertir NSArray a RLMArray con RKValueTransFormer falla al convertir outputValue a AutoreleasingUnsafeMutablePointer <AnyObject?> )

credential.memory = NSURLCredential(trust: trust)

  1. SecPolicyCreateSSL, SecCertificateCreateWithData y SecTrustGetCertificateAtIndex devuelven Unmanaged! objetos, esencialmente hay que convertirlos / salvarlos usando takeRetainedValue () o takeUnretainedValue (). (Ver http://nshipster.com/unmanaged/ ). Tuvimos problemas de memoria / bloqueos cuando usamos takeRetainedValue () y llamamos al método más de una vez (hubo un fallo en SecDestroy). En este momento, la compilación parece estable después de que cambiamos a utilizar takeUnretainedValue (), ya que no necesita los certificados ni las políticas de SSL después de la validación.

  2. TLS caché de sesiones. https://developer.apple.com/library/ios/qa/qa1727/_index.html Eso significa que cuando obtiene una verificación exitosa en un desafío, nunca más vuelve a enfrentar el desafío. Esto realmente puede alterar su cabeza cuando está probando un certificado válido, luego prueba un certificado inválido, que luego omite toda validación y obtiene una respuesta exitosa del servidor. La solución es Product-> Clean en su simulador de iOS después de cada vez que use un certificado válido y pase el desafío de validación. De lo contrario, podría pasar algún tiempo pensando incorrectamente que finalmente obtuvo la CA raíz para validar.

Así que aquí está simplemente una solución de trabajo para los problemas que estaba teniendo con mis servidores. Quería publicar todo aquí para ayudar a alguien más que esté ejecutando un servidor local o de desarrollo con una CA autofirmada y un producto iOS que debe estar habilitado con SSL. Por supuesto, con ATS en iOS 9 espero volver a utilizar SSL muy pronto.

Este código actualmente tiene algunos problemas de administración de memoria y se actualizará en un futuro próximo. Además, si alguien ve esta implementación y dice "Ah, ja, esto es tan malo como devolver VERDADERO para certificados inválidos", ¡por favor avíseme! Por lo que sé a través de nuestras propias pruebas, la aplicación rechaza los certificados de servidor no válidos no firmados por nuestra CA raíz, y acepta el certificado en hoja generado y firmado por la CA raíz. El paquete de aplicaciones solo tiene la CA raíz incluida, por lo que el certificado del servidor se puede cambiar después de su vencimiento y las aplicaciones existentes no fallarán.

Si me meto un poco más en AFNetworking y descubro una solución de una a tres líneas para todo esto (al alternar todas esas pequeñas banderas que proporcionan), también publicaré una actualización.

Si AlamoFire comienza a admitir SSL, también puede publicar una solución aquí.

Esta es una pregunta que trata de encontrar soluciones para mi caso de uso particular y de documentar lo que he intentado hacer para cualquier otra persona que esté siguiendo este proceso.

Tenemos un servidor RESTful y una aplicación para iOS. Tenemos nuestra propia autoridad de certificación y el servidor tiene una autoridad de certificación raíz y un certificado autofirmado. Seguimos este proceso para generar los siguientes archivos:

http://datacenteroverlords.com/2012/03/01/creating-your-own-ssl-certificate-authority/

rootCA.pem rootCA.key server.crt server.key

Solo los certificados de servidor se almacenan en nuestro servidor y, como parte del proceso SSL, las claves públicas se envían con las llamadas de API para su verificación.

He seguido este proceso para usar AFNetworking para usar la fijación de certificados, así como la fijación de claves públicas para verificar nuestros certificados autofirmados:

http://initwithfunk.com/blog/2014/03/12/afnetworking-ssl-pinning-with-self-signed-certificates/

Conforme a esta guía, convertimos el archivo .crt en un archivo .cer (en formato DER):

https://support.ssl.com/Knowledgebase/Article/View/19/0/der-vs-crt-vs-cer-vs-pem-certificates-and-how-to-convert-them

e incluya el archivo .cer (server.cer) en el paquete de aplicaciones de iOS. Esto permite que nuestra aplicación realice solicitudes GET / POST a nuestro servidor. Sin embargo, debido a que nuestro certificado de servidor puede caducar o volver a emitirse, queremos utilizar la CA raíz, como lo hacen las personas en este hilo en AFNetworking:

https://github.com/AFNetworking/AFNetworking/issues/1944

Actualmente, hemos actualizado a AFNetworking 2.6.0, por lo que nuestras bibliotecas de redes deberían incluir todas las actualizaciones, incluidas las de esta discusión:

https://github.com/AFNetworking/AFNetworking/issues/2744

El código utilizado para crear nuestra política de seguridad:

var manager: AFHTTPRequestOperationManager = AFHTTPRequestOperationManager() manager.requestSerializer = AFJSONRequestSerializer() // force serializer to use JSON encoding let policy: AFSecurityPolicy = AFSecurityPolicy(pinningMode: AFSSLPinningMode.PublicKey) var data: [NSData] = [NSData]() for name: String in ["rootCA", "server"] { let path: String? = NSBundle.mainBundle().pathForResource(name, ofType: "cer") let keyData: NSData = NSData(contentsOfFile: path!)! data.append(keyData) } policy.pinnedCertificates = data policy.allowInvalidCertificates = true policy.validatesDomainName = false manager.securityPolicy = policy

Con server.cer incluido, podemos confiar en nuestro servidor fijando la clave pública (también probamos AFSecurityPolicyPinningMode.Certificate); esto funcionó porque el certificado exacto está incluido. Sin embargo, debido a que podríamos cambiar el archivo server.crt que tiene el servidor, queremos poder hacerlo solo con rootCA.cer.

Sin embargo, con solo el rootCA incluido en el paquete de aplicaciones, esto no parece funcionar. ¿Es que el rootCA no tiene suficiente información sobre la clave pública para verificar el certificado del servidor, que se firmó con la CA raíz? El archivo server.crt también puede tener un CommonName cambiante.

Además, dado que mi fluidez en la terminología de SSL es bastante simple, si alguien puede aclarar si estoy haciendo las preguntas correctas, sería genial. Las preguntas específicas son:

  1. ¿Estoy generando los certificados correctamente para que el servidor pueda probar su identidad usando el archivo server.crt autofirmado?
  2. ¿Es posible incluir solo el archivo rootCA.cer en el paquete y poder verificar el servidor de certificados de hoja.crt? ¿Será capaz de verificar otro archivo server2.crt firmado por el mismo rootCA? ¿O deberíamos incluir un certificado intermedio entre rootCA y la hoja?
  3. ¿La clave pública o el certificado están marcando la solución correcta para esto? Todos los foros y publicaciones del blog que he leído dicen que sí, pero incluso con la biblioteca AFNetworking más actualizada, no hemos tenido suerte.
  4. ¿El servidor necesita de alguna manera enviar las firmas server.crt y roomCA.pem?

Si está utilizando los pods de coco, subclasifique la clase AFSecurityPolicy e implemente el control de seguridad de acuerdo con la respuesta de mitrenegade https://.com/a/32469609/4000434

Oye es mi código.

Inicialice AFHttpRequestOperationManager mientras publica la solicitud como se muestra a continuación.

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; manager.responseSerializer = [AFJSONResponseSerializer serializer]; manager.requestSerializer = [AFJSONRequestSerializer serializer]; manager.securityPolicy = [RootCAAFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [manager POST:Domain_Name parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { success(operation,responseObject); [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; NSLog(@"Error %@",error); failure(operation,error); }];

RootCAAFSecurityPolicy es la subclase de AFSecurityPolicy Class. Vea a continuación la clase RootCAAFSecurityPolicy .h y .m que invalida el método

- (BOOL) evalServerTrust: (SecTrustRef) serverTrust forDomain: (NSString *) dominio

Clase RootCAAFSecurityPolicy.h

#import <AFNetworking/AFNetworking.h> @interface RootCAAFSecurityPolicy : AFSecurityPolicy @end

Clase RootCAAFSecurityPolicy.m

Reemplace RootCA con su nombre de archivo de certificado

#import "RootCAAFSecurityPolicy.h" @implementation RootCAAFSecurityPolicy -(BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain { if(self.SSLPinningMode == AFSSLPinningModeCertificate) { return [self shouldTrustServerTrust:serverTrust]; } else { return [super evaluateServerTrust:serverTrust forDomain:domain]; } } - (BOOL)shouldTrustServerTrust:(SecTrustRef)serverTrust { // load up the bundled root CA NSString *certPath = [[NSBundle mainBundle] pathForResource:@"RootCA" ofType:@"der"]; NSAssert(certPath != nil, @"Specified certificate does not exist!"); NSData *certData = [[NSData alloc] initWithContentsOfFile:certPath]; CFDataRef certDataRef = (__bridge_retained CFDataRef)certData; SecCertificateRef cert = SecCertificateCreateWithData(NULL, certDataRef); NSAssert(cert != NULL, @"Failed to create certificate object. Is the certificate in DER format?"); // establish a chain of trust anchored on our bundled certificate CFArrayRef certArrayRef = CFArrayCreate(NULL, (void *)&cert, 1, NULL); OSStatus anchorCertificateStatus = SecTrustSetAnchorCertificates(serverTrust, certArrayRef); NSAssert(anchorCertificateStatus == errSecSuccess, @"Failed to specify custom anchor certificate"); // trust also built-in certificates besides the specified CA OSStatus trustBuiltinCertificatesStatus = SecTrustSetAnchorCertificatesOnly(serverTrust, false); NSAssert(trustBuiltinCertificatesStatus == errSecSuccess, @"Failed to reenable trusting built-in anchor certificates"); // verify that trust SecTrustResultType trustResult; OSStatus evalStatus = SecTrustEvaluate(serverTrust, &trustResult); NSAssert(evalStatus == errSecSuccess, @"Failed to evaluate certificate trust"); // clean up CFRelease(certArrayRef); CFRelease(cert); CFRelease(certDataRef); // did our custom trust chain evaluate successfully return (trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified); } @end