Cifrado AES para un NSString en el iPhone

¿Alguien puede señalarme en la dirección correcta para encriptar una cadena y devolver otra cadena con los datos encriptados? (He estado intentando con el cifrado AES256). Quiero escribir un método que requiera dos instancias de NSString, una es el mensaje para encriptar y la otra es una ‘contraseña’ para encriptarla. Sospecho que tendré que generar la clave de encriptación con el código de acceso, de una manera que se puede revertir si el código de acceso se proporciona con los datos encriptados. El método debería devolver un NSString creado a partir de los datos cifrados.

Intenté la técnica detallada en el primer comentario de esta publicación , pero hasta ahora no he tenido suerte. CryptoExercise de Apple ciertamente tiene algo, pero no puedo encontrarle sentido … He visto muchas referencias a CCCrypt , pero ha fallado en todos los casos en que lo he usado.

También tendría que poder descifrar una cadena encriptada, pero espero que sea tan simple como kCCEncrypt / kCecrypt.

Como no ha publicado ningún código, es difícil saber exactamente qué problemas está encontrando. Sin embargo, la publicación de blog a la que se vincula parece funcionar bastante bien … aparte de la coma adicional en cada llamada a CCCrypt() que provocó errores de comstackción.

Un comentario posterior sobre esa publicación incluye este código adaptado , que funciona para mí y parece un poco más directo. Si incluye su código para la categoría NSData, puede escribir algo como esto: (Nota: Las llamadas printf() son solo para demostrar el estado de los datos en varios puntos: en una aplicación real, no tendría sentido imprima tales valores.)

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

Dado este código, y el hecho de que los datos cifrados no siempre se traducen bien en un NSString, puede ser más conveniente escribir dos métodos que envuelvan la funcionalidad que necesita, en avance y retroceso …

 - (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]; } 

Esto definitivamente funciona en Snow Leopard, y @Boz informa que CommonCrypto es parte del Core OS en el iPhone. Tanto 10.4 como 10.5 tienen /usr/include/CommonCrypto , aunque 10.5 tiene una página man para CCCryptor.3cc y 10.4 no, por lo que YMMV.


EDIT: vea esta pregunta de seguimiento sobre el uso de la encoding Base64 para representar bytes de datos cifrados como una cadena (si lo desea) utilizando conversiones seguras y sin pérdidas.

He reunido una colección de categorías para NSData y NSString que utiliza soluciones que se encuentran en el blog de Jeff LaMarche y algunas sugerencias de Quinn Taylor aquí en Stack Overflow.

Utiliza categorías para extender NSData para proporcionar el cifrado AES256 y también ofrece una extensión de NSString a BASE64-codificar datos cifrados de forma segura en cadenas.

Aquí hay un ejemplo para mostrar el uso para encriptar cadenas:

 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] ); 

Obtenga el código fuente completo aquí:

https://gist.github.com/838614

¡Gracias por todos los consejos útiles!

— Miguel

@owlstead, con respecto a su solicitud de “una variante criptográficamente segura de una de las respuestas dadas,” vea RNCryptor . Fue diseñado para hacer exactamente lo que está solicitando (y fue creado en respuesta a los problemas con el código que se detalla aquí).

RNCryptor utiliza PBKDF2 con sal, proporciona una IV aleatoria y conecta HMAC (también generado a partir de PBKDF2 con su propia sal. Admite la operación síncrona y asincrónica.

Esperé un poco en @QuinnTaylor para actualizar su respuesta, pero como no lo hizo, aquí está la respuesta un poco más clara y de una manera que se cargará en XCode7 (y tal vez mayor). Lo usé en una aplicación Cocoa, pero probablemente también funcionará bien con una aplicación iOS. No tiene errores de ARC.

Pegue antes de cualquier sección @implementation en su archivo AppDelegate.m o AppDelegate.mm.

 #import  @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 

Pega estas dos funciones en la clase @implementation que deseas. En mi caso, elegí @implementation AppDelegate en mi archivo 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]; }