2011-03-16 16 views
16

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 al https://mysite. ¿Hay alguna manera de que pueda instalar/guardar el certificado mediante una solicitud posterior en el llavero? O Copio ese certificado (el archivo) al paquete de recursos para marcarlo de confianza.iOS: Certificado SSL de instalación previa en llavero - programáticamente

gracias

al

+3

Debe aceptar una respuesta o aclarar sus inquietudes si no resolvieron sus problemas. – MrTJ

Respuesta

14

vez que tenga el certificado de 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); 
} 

Se añade el certificado a la caja de arena llavero de su aplicación, es decir ninguna otra aplicación confiará en tu certificado.

+2

Gracias.Sin embargo, al hacer esto, no autenticará el servidor autofirmado automáticamente, por favor vea [mi respuesta] (http://stackoverflow.com/a/21621522/1432048) a otra pregunta. – xiang

7

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

Usted tiene dos opciones disponibles: agregar el certificado del servidor para el llavero o realizar la validación manual. 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 del servidor es parte de una cadena a una autoridad de certificación raíz, se debe instalar la entidad emisora ​​de certificados raíz en lugar de un certificado de 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 CFLívelo cuando ya no lo necesite para evitar pérdidas de memoria.

A continuación, puede agregar su cert 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 sólo desea verificar el certificado para la conexión de la que está creando y no para otros enchufes de tu aplicación, se puede verificar su confianza en el certificado manualmente. En primer lugar, crear un socket (asumiendo que su servidor está escuchando en el puerto 8443 en el mismo equipo que el cliente) y desactivar su validación cadena de certificados en la 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); 
} 

Entonces, cuando se recibe una llamada de retorno que su toma 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; 
    } 
} 
Cuestiones relacionadas