2011-03-16 12 views
16

Voglio installare/salvare un certificato nel portachiavi prima che l'utente visiti il ​​sito. Ho un server HTTPS e la mia app autentica l'utente prima di andare allo https://mysite. Esiste un modo per installare/salvare il certificato tramite richiesta post nel portachiavi. O Copia il certificato (il file) in un gruppo di risorse per contrassegnarlo come affidabile.iOS: pre installare il certificato SSL nel portachiavi - al livello di programmazione

grazie

al

+3

È necessario accettare una risposta o chiarire le proprie preoccupazioni se non hanno risolto i problemi. – MrTJ

risposta

14

Una volta ottenuto il certificato del server in formato der si può provare il seguente codice:

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

Si aggiungerà il certificato alla sandbox portachiavi del vostro cioè applicazione nessun'altra applicazione si fiderà del tuo cert.

+2

Grazie.Tuttavia, fare questo alow non autentificherà automaticamente il server autofirmato, per favore vedi [la mia risposta] (http://stackoverflow.com/a/21621522/1432048) ad un'altra domanda. – xiang

7

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

Sono disponibili due opzioni disponibili: aggiungere il certificato del server al portachiavi o eseguire la convalida manualmente. Indipendentemente dal tuo approccio, dovrai includere un certificato pubblico X.509 con codifica DER nella tua app. Nell'esempio seguente, è denominato "ios-trusted-cert.der") e crea un SecCertificateRef con esso. (Se il certificato del server è parte di una catena di un'autorità di certificazione, è necessario installare l'autorità di certificazione principale piuttosto che il certificato del server.)

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

Ricordate che SecCertificateCreateWithData segue la regola di creare di proprietà di memoria, in modo da CFRilasciarlo quando non ne hai più bisogno per evitare perdite di memoria.

Successivamente, è possibile aggiungere il certificato al portachiavi dell'app. Questo è appropriato quando vuoi che iOS si fidi del tuo certificato per ogni nuovo socket che crei.

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

Se si desidera solo per verificare il CERT per la presa che si sta creando e per nessun altro prese del tuo app, è possibile verificare la tua fiducia nel cert manualmente. In primo luogo, creare un socket (supponendo che il server è in ascolto sulla porta 8443 sulla stessa macchina come client) e disabilitare sua validazione catena di certificati nelle sue impostazioni 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); 
} 

Poi, quando si riceve un callback che la presa è pronto per scrivere i dati, è necessario verificare la fiducia nel certificato incluso nel server prima di scrivere qualsiasi dato o leggere qualsiasi dato dal server. Primo (1), creare un criterio SSL client con il nome host del server a cui si è connessi. Il nome host è incluso nel certificato del server per autenticare che il server a cui il DNS ha diretto l'utente è il server di cui ti fidi. Successivo (2), si prendono i certificati del server effettivo dal socket. Potrebbero esserci più certificati associati al server se il certificato del server fa parte di una catena di certificati. Quando hai i certificati server effettivi, puoi (3) creare un oggetto trust. L'oggetto trust rappresenta un contesto locale per le valutazioni di trust. Isola le singole valutazioni di trust mentre i certificati portachiavi si applicano a tutti i socket fidati. Dopo aver avuto un trust object, puoi (4) impostare i certificati di ancoraggio, che sono i certificati di cui ti fidi. Infine (5), è possibile valutare l'oggetto trust e scoprire se il server può essere considerato affidabile.

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