iOS: Certificado SSL de instalación previa en llavero – programáticamente

Quiero instalar / guardar un certificado en llavero antes de que el usuario visite el sitio. Tengo un servidor HTTPS y mi aplicación autentica al usuario antes de ir a https: // mysite . ¿Hay alguna manera de que pueda instalar / guardar el certificado mediante una solicitud posterior en el llavero? O copie ese certificado (el archivo) en el paquete de recursos para marcarlo de confianza.

Gracias

Alabama

Una vez que tenga el certificado del servidor en formato der, puede probar el siguiente código:

+ (void) addCertToKeychain:(NSData*)certInDer { OSStatus err = noErr; SecCertificateRef cert; cert = SecCertificateCreateWithData(NULL, (CFDataRef) certInDer); assert(cert != NULL); CFTypeRef result; NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys: (id)kSecClassCertificate, kSecClass, cert, kSecValueRef, nil]; err = SecItemAdd((CFDictionaryRef)dict, &result); assert(err == noErr || err == errSecDuplicateItem); CFRelease(cert); } 

Agregará el certificado al arenero de llavero de su aplicación, es decir, ninguna otra aplicación confiará en su certificado.

De: http://blog.asolutions.com/2011/02/using-tls-with-self-signed-certificates-or-custom-root-certificates-in-ios/

Tiene dos opciones disponibles: agregue el certificado de su servidor al llavero o realice la validación manualmente. Independientemente de su enfoque, deberá incluir un certificado público X.509 codificado DER en su aplicación. En el siguiente ejemplo, se llama “ios-trusted-cert.der” y crea un SecCertificateRef con él. (Si el certificado de su servidor es parte de una cadena a una autoridad de certificación raíz, debe instalar la autoridad del certificado raíz en lugar del certificado del servidor).

 NSBundle *bundle = [NSBundle bundleForClass:[self class]]; NSData *iosTrustedCertDerData = [NSData dataWithContentsOfFile:[bundle pathForResource:@"ios-trusted-cert" ofType:@"der"]]; SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (CFDataRef) iosTrustedCertDerData); 

Recuerde que SecCertificateCreateWithData sigue la regla de creación de la propiedad de la memoria, por lo que debe CFLiberarlo cuando ya no lo necesite para evitar pérdidas de memoria.

Luego, puede agregar su certificado al llavero de su aplicación. Esto es apropiado cuando desea que iOS confíe en su certificado para cada nuevo socket que cree.

 - (void) useKeychain: (SecCertificateRef) certificate { OSStatus err = SecItemAdd((CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys: (id) kSecClassCertificate, kSecClass, certificate, kSecValueRef, nil], NULL); if ((err == noErr) || // success! (err == errSecDuplicateItem)) { // the cert was already added. Success! // create your socket normally. // This is oversimplified. Refer to the CFNetwork Guide for more details. CFReadStreamRef readStream; CFWriteStreamRef writeStream; CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"localhost", 8443, &readStream, &writeStream); CFReadStreamSetProperty(readStream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelTLSv1); CFReadStreamOpen(readStream); CFWriteStreamOpen(writeStream); } else { // handle the error. There is probably something wrong with your cert. } } 

Si solo desea verificar el certificado para el socket que está creando y para ningún otro socket en su aplicación, puede verificar su confianza en el certificado de forma manual. Primero, cree un socket (suponiendo que su servidor esté escuchando en el puerto 8443 en la misma máquina que su cliente) y desactive la validación de la cadena de certificados en su configuración de ssl:

 - (void) verifiesManually: (SecCertificateRef) certificate { CFReadStreamRef readStream; CFWriteStreamRef writeStream; CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"localhost", 8443, &readStream, &writeStream); // Set this kCFStreamPropertySocketSecurityLevel before // setting kCFStreamPropertySSLSettings. // Setting kCFStreamPropertySocketSecurityLevel // appears to override previous settings in kCFStreamPropertySSLSettings CFReadStreamSetProperty(readStream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelTLSv1); // this disables certificate chain validation in ssl settings. NSDictionary *sslSettings = [NSDictionary dictionaryWithObjectsAndKeys: (id)kCFBooleanFalse, (id)kCFStreamSSLValidatesCertificateChain, nil]; CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, sslSettings); NSInputStream *inputStream = (NSInputStream *)readStream; NSOutputStream *outputStream = (NSOutputStream *)writeStream; [inputStream setDelegate:self]; [outputStream setDelegate:self]; [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; CFReadStreamOpen(readStream); CFWriteStreamOpen(writeStream); } 

Luego, cuando reciba una callback de que su socket está listo para escribir datos, debe verificar la confianza en el certificado incluido en su servidor antes de escribir cualquier dato o leer datos del servidor. Primero (1), cree una política de SSL del cliente con el nombre de host del servidor al que se conectó. El nombre de host se incluye en el certificado del servidor para autenticar que el servidor al que se lo dirigió DNS es el servidor en el que confía. Luego (2), toma los certificados del servidor real del socket. Puede haber varios certificados asociados con el servidor si el certificado del servidor es parte de una cadena de certificados. Cuando tiene los certificados de servidor reales, puede (3) crear un objeto de confianza. El objeto de confianza representa un contexto local para las evaluaciones de confianza. Aísla las evaluaciones de confianza individuales, mientras que los certificados de llavero se aplican a todos los sockets de confianza. Después de tener un objeto de confianza, puede (4) configurar los certificados de anclaje, que son los certificados en los que confía. Finalmente (5), puede evaluar el objeto de confianza y descubrir si se puede confiar en el servidor.

 #pragma mark - #pragma mark NSStreamDelegate - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { switch (eventCode) { case NSStreamEventNone: break; case NSStreamEventOpenCompleted: break; case NSStreamEventHasBytesAvailable: break; case NSStreamEventHasSpaceAvailable: // #1 // NO for client, YES for server. In this example, we are a client // replace "localhost" with the name of the server to which you are connecting SecPolicyRef policy = SecPolicyCreateSSL(NO, CFSTR("localhost")); SecTrustRef trust = NULL; // #2 CFArrayRef streamCertificates = [aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates]; // #3 SecTrustCreateWithCertificates(streamCertificates, policy, &trust); // #4 SecTrustSetAnchorCertificates(trust, (CFArrayRef) [NSArray arrayWithObject:(id) self.certificate]); // #5 SecTrustResultType trustResultType = kSecTrustResultInvalid; OSStatus status = SecTrustEvaluate(trust, &trustResultType); if (status == errSecSuccess) { // expect trustResultType == kSecTrustResultUnspecified // until my cert exists in the keychain see technote for more detail. if (trustResultType == kSecTrustResultUnspecified) { NSLog(@"We can trust this certificate! TrustResultType: %d", trustResultType); } else { NSLog(@"Cannot trust certificate. TrustResultType: %d", trustResultType); } } else { NSLog(@"Creating trust failed: %d", status); [aStream close]; } if (trust) { CFRelease(trust); } if (policy) { CFRelease(policy); } break; case NSStreamEventErrorOccurred: NSLog(@"unexpected NSStreamEventErrorOccurred: %@", [aStream streamError]); break; case NSStreamEventEndEncountered: break; default: break; } }