thxou

Ama la sabiduría. Desea el conocimiento.

Encriptar Strings Usando Encriptación AES-256 en Objective-C

| Comments

Esta semana, mientras trabajaba en una aplicación, tuve la idea de “ocultar” algunos datos en forma de texto que no quería que fuesen vistos. Concretamente trabajaba con Core Data, y pues, para incrementar el nivel de seguridad, decidí encriptar ciertos datos que guardaba en Core Data, por el hecho de que cualquier usuario con Jailbreak puede ver los datos de Core Data que no han sido encriptados sin ningún problema. Esto me llevo a buscar algunas soluciones y pues, di con la encriptación AES256.

AES (Advanced Encryption Standard), es un algoritmo de encriptación de tipo simétrico. Esto quiere decir que se necesita una clave tanto para cifrar como para descifrar los datos encriptados. Esta clave puede ser cualquier texto que no exceda los 256 bits, pero obviamente que mientras más compleja sea la clave, más difícil será de romper mediante ataques fuerza bruta. De cara a que tu aplicación implemente capas de seguridad con cifrado, la elección de la clave es muy importante. Por ejemplo, aplicaciones geniales como 1Password utilizan este tipo de encriptación para sus datos, ya que tratan con datos tan sencibles como las tarjetas de crédito, contraseñas, etc.

Creando Nuestra Categoría

Afortunadamente por internete campan unos cuantos algoritmos para implementar este tipo de encriptación en Objective-C, de manera que no tienes que implentar tu propio algoritmo, o si, a menos que seas experto en encriptación y busques hacer algo diferente. Yo encontré una categoría de Objective-C que hace el trabajo, lamentablemente he buscado y no he podido encontrar al autor, no obstante si lo llegas a encontrar tu, no dudes en comunicarlo.

Si sigues el enlace notarás que tenemos que crear la categoría para usarla en nuestro propio proyecto. Yo ya la he creado, por lo que podrás encontrarla ya implementada en el proyecto de ejemplo de este tutorial. Tan solo tienes que copiar los ficheros NSData+AES256.h y NSData+AES256.m en tu propio proyecto y ya puedes empezar.

Jugando con la categoría

Primero que todo necesitamos importar el fichero de cabeceras para que nuestros nuevos métodos de NSData corran tranquilamente en nuestro proyecto:

1
#import "NSData+AES256.h"

Esta categoría tiene un par de método que nos servirán para encriptar y desencriptar contenido. Usan una clase llamada CommonCrypto creada por Apple y provista como una interfaz genérica para tipos de encriptación simétrica, como es el caso de AES. Entonces estos métodos son:

1
2
- (NSData *)AES256EncryptWithKey:(NSString *)key;
- (NSData *)AES256DecryptWithKey:(NSString *)key;

Hablemos del primero. Este meétodo devuelve un objeto NSData con el texto ya encriptado. Al ser el método de una categoría de NSData, necesitamos pasar nuestro string a encriptar a un objeto NSData. Con esto ya tendríamos casi hecho todo. No obstante, cuando trabajamos con Core Data, NSData es uno de los tipos que requiere algo más de trabajo al momento de usarlo, por lo que nos va a convenir más trabajar con objetos NSString que son más fáciles de manipular. Así que vamos a tener que pasar ese NSData a NSString:

1
2
3
4
5
NSString *textToEncrypt = @"A veces sueño que soy programador";
NSData *textData = [textToEncrypt dataUsingEncoding:NSUTF8StringEncoding];
NSData *encryptedData = [textData AES256EncryptWithKey:kEncryptionKey];
NSString *encryptedString = [[NSString alloc] initWithData:textData encoding:NSUTF8StringEncoding];
NSLog(@"Encrypted String: %@", encryptedString);

kEncryptionKey es la clave que vas a usar para encriptar el contenido. Hay muchos sitios web que te ayudan a crear claves seguras, también puedes hacer tu propio algoritmo, eso depende de ti. En el objeto encryptedData tienes el texto encriptado. Al probar este código te darás cuenta que el NSLog no imprime nada. Esto es porque porque los datos encriptados con AES no se traducen bien a strings planos, por lo que puede que simplemente recibas una cadena null. De cara a trabajar con Core Data, es buena idea pasar el string encriptado a base64 ya que los string en base64 si que son completamente representables como caracteres ASCII y así nos evitamos cualquier inconveniente. Entonces podríamos quitar las 2 últimas líneas del código anterior y poner lo siguiente:

1
2
NSString *base64String = [encryptedData base64EncodedStringWithOptions:0];
// luego puedes hacer un NSLog o guardarlo en Core Data directamente

A parte de hacer la cadena encriptada fácilmente representable como string, pasarlo a base64 añade una capa más de encriptación que en algunos casos podría servir para confundir al que intente adivinar el texto. Pasarle 0 como opción, quiere decir que no queremos definir ninguna opción explicitamente.

Para desencriptar nuestro texto hay que proceder de manera inversa. Decodificamos el string codificado en base64 y lo pasamos a un objeto NSData, acto seguido procedemos a desencriptar los datos usando el segundo de los 2 métodos mencionados arriba y la misma clave que usamos para encriptar el texto, y luego pasamos el NSData resultante a NSString, ya sea para mostrarlo por pantalla o para lo que haga falta:

1
2
3
4
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:stream.url options:0];
NSData *decryptedData = [decodedData AES256DecryptWithKey:kEncryptionKey];
NSString *decryptedString = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];
NSLog(@"Decrypted String: %@", decryptedString);

El proyecto de ejemplo que hice para este tutorial sigue exactamente el mismo patrón que he explicado anteriormente. La diferencia es que en el proyecto, una vez encriptado el texto, lo que hago es guardarlo en una entidad de Core Data, luego recupero estos datos y procedo a desencriptarlo y a mostrarlo con un NSLog:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// procedemos a encriptar el texto e imprimir el resultado
NSString *textToEncrypt = @"contraseñasupersecreta";
NSData *textData = [textToEncrypt dataUsingEncoding:NSUTF8StringEncoding];
NSData *encryptedData = [textData AES256EncryptWithKey:kEncryptionKey];
NSString *base64EncryptedPassword = [encryptedData base64EncodedStringWithOptions:0];
NSLog(@"Contraseña Encriptada: %@", base64EncryptedPassword);

// creamos el objeto modelo y lo guardamos
User *user = [NSEntityDescription insertNewObjectForEntityForName:@"User"
                                           inManagedObjectContext:self.managedObjectContext];
user.username = @"ThXou";
user.password = base64EncryptedPassword;
[self saveContext];

// ahora recuperamos el objeto que guardamos antes
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"User"];
NSArray *users = [self.managedObjectContext executeFetchRequest:request error:nil];

// desencriptamos el texto guardado y lo mostramos en pantalla
User *encryptedUser = users[0];
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:encryptedUser.password options:0];
NSData *decryptedData = [decodedData AES256DecryptWithKey:kEncryptionKey];
NSString *decryptedString = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];
NSLog(@"Contraseña Desencriptada: %@", decryptedString);

Si aún no te has descargado el proyecto de ejemplo, hazlo desde este enlace, luego ábrelo y ejecútalo. No hay configurado nada de la interfaz gráfica, por lo que solo verás los resultados en el debugger. Si todo va bien (debería), el resultado será el siguiente:

1
2
Contraseña Encriptada: JkDSEvGtzfygH8VxS3F9scomwCrdWFAHVBIbh8TpaYA=
Contraseña Desencriptada: contraseñasupersecreta

En la primera línea se puede ver la contraseña encriptada pero codificada en base64, y en la segunda la contraseña ya desencriptada. Si quieres ver el contenido codificado con base64, puedes usar alguna de las webs que hay por ahí, yo he usado esta, pero tu puedes usar la que quieras.

Un par de notas finales

Esta solución de encriptación está muy bien ya que es fácil de implementar, no requiere ningún framework o librería externa porque usa las que vienen predefinidas en el sistema, pero sobre todo porque podría pasar los procesos de validación en la App Store sin tener que especificar o certificar la encriptacion.

Por motivos de organización, también podrías crear una nueva categoría de NSString que añada un par de métodos que te devuelvan los strings directamente en vez de tratar con NSData, sobre todo te va a ser útil si tienes que implementar encriptación en varias partes de tu proyecto.

Comments