2009-09-09 17 views
118

Qualcuno può indicarmi la giusta direzione per essere in grado di crittografare una stringa, restituendo un'altra stringa con i dati crittografati? (Ho provato con la crittografia AES256.) Voglio scrivere un metodo che richiede due istanze NSString, una delle quali è il messaggio da cifrare e l'altra è un "passcode" per crittografarla con - sospetto che dovrei generare la chiave di crittografia con il codice di accesso, in un modo che può essere invertito se il codice di accesso viene fornito con i dati crittografati. Il metodo dovrebbe quindi restituire una NSString creata dai dati crittografati.Crittografia AES per una NSString su iPhone

Ho provato la tecnica dettagliata in the first comment on this post, ma finora non ho avuto fortuna. Il numero CryptoExercise di Apple ha sicuramente qualcosa, ma non riesco a capirlo ... Ho visto molti riferimenti allo CCCrypt, ma in ogni caso l'ho usato.

Dovrei anche essere in grado di decodificare una stringa crittografata, ma spero che sia semplice come kCCEncrypt/kCCDecrypt.

+1

prega di notare che ho dato una taglia ad una risposta da Rob Napier che ha fornito una versione ** ** sicuro della risposta. –

risposta

124

Dato che non hai pubblicato alcun codice, è difficile sapere esattamente quali problemi si verificano. Tuttavia, il post del blog a cui ti colleghi sembra funzionare piuttosto decentemente ... a parte la virgola extra in ogni chiamata a CCCrypt() che ha causato errori di compilazione.

Un commento successivo su questo post include this adapted code, che funziona per me e sembra un po 'più semplice. Se si include il codice per la categoria NSData, è possibile scrivere qualcosa di simile: (Nota: le chiamate printf() servono solo a dimostrare lo stato dei dati in vari punti: in un'applicazione reale, non avrebbe senso stampare tali valori.)

int main (int argc, const char * argv[]) { 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    NSString *key = @"my password"; 
    NSString *secret = @"text to encrypt"; 

    NSData *plain = [secret dataUsingEncoding:NSUTF8StringEncoding]; 
    NSData *cipher = [plain AES256EncryptWithKey:key]; 
    printf("%s\n", [[cipher description] UTF8String]); 

    plain = [cipher AES256DecryptWithKey:key]; 
    printf("%s\n", [[plain description] UTF8String]); 
    printf("%s\n", [[[NSString alloc] initWithData:plain encoding:NSUTF8StringEncoding] UTF8String]); 

    [pool drain]; 
    return 0; 
} 

Dato questo codice, e il fatto che i dati crittografati non sempre si traduce bene in un NSString, può essere più conveniente di scrivere due metodi che avvolgono le funzionalità necessarie, in avanti e indietro. ..

- (NSData*) encryptString:(NSString*)plaintext withKey:(NSString*)key { 
    return [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key]; 
} 

- (NSString*) decryptData:(NSData*)ciphertext withKey:(NSString*)key { 
    return [[[NSString alloc] initWithData:[ciphertext AES256DecryptWithKey:key] 
            encoding:NSUTF8StringEncoding] autorelease]; 
} 

Questo funziona sicuramente su Snow Leopard, e @Boz segnala che CommonCrypto fa parte del sistema operativo principale su iPhone. Entrambi 10.4 e 10.5 hanno /usr/include/CommonCrypto, sebbene 10.5 abbia una pagina man per CCCryptor.3cc e 10.4 no, quindi YMMV.


EDIT: Vedere this follow-up question su utilizzando la codifica Base64 per rappresentare byte di dati criptati come una stringa (se desiderato) utilizzando sicure, conversioni senza perdita.

+1

Grazie. CommonCrypto fa parte del sistema operativo principale su iPhone e anch'esso eseguo il 10.6. – Boz

+0

Bella risposta Quinn. – GSD

+0

Ho fatto -1, perché il codice di riferimento è pericolosamente insicuro. Guarda invece la risposta di Rob Napier. Il suo blog "http://robnapier.net/aes-commoncrypto spiega esattamente perché questo è insicuro –

45

Ho creato una raccolta di categorie per NSData e NSString che utilizza soluzioni trovate su e some hints di Quinn Taylor qui su Stack Overflow.

Utilizza categorie per estendere NSData per fornire la crittografia AES256 e offre anche un'estensione di NSString per BASE64 - codificare i dati crittografati in modo sicuro per le stringhe.

Ecco un esempio per mostrare l'utilizzo per le stringhe crittografia:

NSString *plainString = @"This string will be encrypted"; 
NSString *key = @"YourEncryptionKey"; // should be provided by a user 

NSLog(@"Original String: %@", plainString); 

NSString *encryptedString = [plainString AES256EncryptWithKey:key]; 
NSLog(@"Encrypted String: %@", encryptedString); 

NSLog(@"Decrypted String: %@", [encryptedString AES256DecryptWithKey:key]); 

ottenere il codice sorgente completo qui:

https://gist.github.com/838614

Grazie per tutti i suggerimenti utili!

- Michael

+4

ottimo codice !! Grazie per i suggerimenti ... – Nick

+0

NSString * key = @ "YourEncryptionKey"; // dovrebbe essere fornito da un utente Possiamo generare una chiave a 256 bit sicura a caso, invece di una fornita dall'utente. –

+0

Il collegamento di Jeff LaMarche è rotto – whyoz

31

@owlstead, per quanto riguarda la sua richiesta di "una variante crittograficamente sicuro di una delle risposte date," per favore vedi RNCryptor. È stato progettato per fare esattamente quello che stai richiedendo (ed è stato creato in risposta ai problemi con il codice elencato qui).

RNCryptor utilizza PBKDF2 con sale, fornisce un IV casuale, e attribuisce HMAC (generato anche da PBKDF2 con il proprio sale. Esso supporta il funzionamento sincrono e asincrono.

+0

Codice interessante e probabilmente vale i punti. Qual è il conteggio delle iterazioni per PBKDF2 e su cosa calcoli l'HMAC? Presumo solo i dati crittografati? Non sono riuscito a trovarlo facilmente nella documentazione fornita. –

+0

Guarda "Sicurezza best practice" per i dettagli. Raccomando 10k iterations su iOS (~ 80ms su un iPhone 4). E sì, encrypt-than-HMAC. Probabilmente guarderò stasera la pagina "Formato dati" per assicurarmi che sia aggiornata sulla v2.0 (i documenti principali sono aggiornati, ma non ricordo se ho modificato la pagina del formato dei dati). –

+0

Ah, sì, ho trovato il numero di round nei documenti e ho visto il codice. Vedo funzioni di pulizia e separato HMAC e chiavi di crittografia. Se il tempo lo consente, cercherò di dare un'occhiata più approfondita domani. Quindi assegnerò i punti. –

7

Ho aspettato un po @QuinnTaylor aggiornare la sua risposta , ma poiché non lo ha fatto, ecco la risposta un po 'più chiara e in un modo che verrà caricata su XCode7 (e forse più grande). L'ho usato in un'applicazione Cocoa, ma probabilmente funzionerà bene con un'applicazione iOS come bene. non ha errori di ARC.

Incolla prima di ogni sezione @implementation nel AppDelegate.m o il file AppDelegate.mm.

#import <CommonCrypto/CommonCryptor.h> 

@implementation NSData (AES256) 

- (NSData *)AES256EncryptWithKey:(NSString *)key { 
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise 
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused) 
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding) 

    // fetch key data 
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; 

    NSUInteger dataLength = [self length]; 

    //See the doc: For block ciphers, the output size will always be less than or 
    //equal to the input size plus the size of one block. 
    //That's why we need to add the size of one block here 
    size_t bufferSize = dataLength + kCCBlockSizeAES128; 
    void *buffer = malloc(bufferSize); 

    size_t numBytesEncrypted = 0; 
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 
            keyPtr, kCCKeySizeAES256, 
            NULL /* initialization vector (optional) */, 
            [self bytes], dataLength, /* input */ 
            buffer, bufferSize, /* output */ 
            &numBytesEncrypted); 
    if (cryptStatus == kCCSuccess) { 
     //the returned NSData takes ownership of the buffer and will free it on deallocation 
     return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; 
    } 

    free(buffer); //free the buffer; 
    return nil; 
} 

- (NSData *)AES256DecryptWithKey:(NSString *)key { 
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise 
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused) 
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding) 

    // fetch key data 
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; 

    NSUInteger dataLength = [self length]; 

    //See the doc: For block ciphers, the output size will always be less than or 
    //equal to the input size plus the size of one block. 
    //That's why we need to add the size of one block here 
    size_t bufferSize = dataLength + kCCBlockSizeAES128; 
    void *buffer = malloc(bufferSize); 

    size_t numBytesDecrypted = 0; 
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 
            keyPtr, kCCKeySizeAES256, 
            NULL /* initialization vector (optional) */, 
            [self bytes], dataLength, /* input */ 
            buffer, bufferSize, /* output */ 
            &numBytesDecrypted); 

    if (cryptStatus == kCCSuccess) { 
     //the returned NSData takes ownership of the buffer and will free it on deallocation 
     return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted]; 
    } 

    free(buffer); //free the buffer; 
    return nil; 
} 

@end 

Incolla queste due funzioni nella classe @implementation desiderata. Nel mio caso, ho scelto @implementation AppDelegate nel mio file AppDelegate.mm o AppDelegate.m.

- (NSString *) encryptString:(NSString*)plaintext withKey:(NSString*)key { 
    NSData *data = [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key]; 
    return [data base64EncodedStringWithOptions:kNilOptions]; 
} 

- (NSString *) decryptString:(NSString *)ciphertext withKey:(NSString*)key { 
    NSData *data = [[NSData alloc] initWithBase64EncodedString:ciphertext options:kNilOptions]; 
    return [[NSString alloc] initWithData:[data AES256DecryptWithKey:key] encoding:NSUTF8StringEncoding]; 
} 
+0

Nota: 1. In decrittografia, la dimensione dell'output sarà inferiore alla dimensione di input quando c'è il padding (PKCS # 7). Non c'è motivo per aumentare il bufferSize, basta usare la dimensione dei dati crittografati. 2. Invece di eseguire il mallocing di un buffer e quindi 'dataWithBytesNoCopy' basta allocare un' NSMutableData' con 'dataWithLength' e usare la proprietà' mutableBytes' per il puntatore di byte e quindi ridimensionare semplicemente impostando la sua proprietà 'length'. 3. L'uso della stringa direttamente per una crittografia è molto insicuro, è necessario utilizzare una chiave derivata come creata da PBKDF2. – zaph

+0

@zaph, puoi fare un pastebin/pastie da qualche parte in modo che possa vedere le modifiche? A proposito, nel codice sopra, ho semplicemente adattato il codice che ho visto da Quinn Taylor per farlo funzionare. Sto ancora imparando questo business mentre vado, e il tuo contributo mi sarà molto utile. – Volomike

+0

Vedere questo [risposta SO] (http://stackoverflow.com/a/23641521/451475) e ha anche una gestione minima degli errori e gestisce sia la crittografia che la decrittografia. Non è necessario estendere il buffer alla decrittografia, è solo meno codice che non si specializza con un ulteriore se quando c'è poco da guadagnare. Nel caso in cui si desideri estendere la chiave con nulls (che non dovrebbe essere fatto) basta creare una versione mutabile della chiave e impostare la lunghezza: 'keyData.length = kCCKeySizeAES256;'. – zaph

Problemi correlati