Saturday, May 10, 2014

RNCryptor - Easy encryption for iOS

Let me start this post off by saying that, like most developers, I am not an encryption expert.  Actually on a scale of one to ten, I would give myself a two and a half and the only reason I would give myself that high of a score is because I can spell encryption correctly.  Therefore when I was looking to put encryption into an iOS application that I was building I was looking for the big red easy button and what I found was the RNCryptor library.

You may be asking why we are putting an article about encryption in a network development blog.  Well encryption can be used in almost all applications but it is especially important in network applications if you want to secure the information that is passed between two devices.   In this post we will show how to include RNCryptor in your iOS project and how to encrypt/save a NSDictionary object to the application’s document directory.

We will begin by downloading the source for the RBCryptor library.  You can download it with this link.  Once the code is downloaded you can simply drop the source files into your project.  The main page also has instructions on building the project.

Lets dive right into the code and encrypt/save a NSDictionary object to a file.  Anyone that has tried to write their own encryption library knows that this is not a trivial process but with RNCryptor we can do this with just a few lines of code.  You will need to import the RNEncryptor.h header file into your class.

+(bool)saveDictionary:(NSDictionary *)dict toFile:(NSString*)filePath withKey:(NSString *)key {
    NSData *dictData = [NSKeyedArchiver archivedDataWithRootObject:dict];
    NSError *error;
    NSData *encryptedData = [RNEncryptor encryptData:dictData withSettings:kRNCryptorAES256Settings password:key error:&error];
    if (error != nil)
        return FALSE;
    else
        return [encryptedData writeToFile:filePath atomically:YES];
}

In this method we begin by using the NSKeyedArchiver to convert our NSDictionary object to an NSData object.  We then used RNEncryptor’s encryptData:withSettings:password:error: static method to encrypt the data.  If there were no issues encrypting the data, the encryptedData object will contain the encrypted data and the error object will be nil.  Therefore checking that the error object is nil verifies that the data was correctly encrypted.  If the data was encrypted correctly we save it to a file.  That was definitely a lot easier than rolling your own encryption or using Apple’s Cryptographic services.

Now lets look at how we would read the file and decrypt the NSDictionary. 

+(NSDictionary*)getDictionaryFromFile:(NSString*)filePath withKey:(NSString *)key {
    NSData *encryptedData = [NSData dataWithContentsOfFile:filePath];
    NSError *error;
    NSData *dictData = [RNDecryptor decryptData:encryptedData withPassword:key error:&error];
    if (error != nil)
        return nil;
    else
        return (NSDictionary *)[NSKeyedUnarchiver unarchiveObjectWithData:dictData];
}

We start the getDictionaryFromFile:withKey: method by using NSData’s dataWithContentsOfFile: method to read the file that contains our encrypted NSDictionary.  Next we use RNDecryptor’s decryptData:WithPassword:error: static method to decrypt the NSData object.  Just like the encryptData:withSettings:password:error: method, if there were no issues the data the dictData object will contain the unencrypted data and the error object will be nil.  Therefore checking that the error object is not nil verifies that there were no issues with the decryption.  If the data was decrypted correctly, we use the NSKeyedUnarchiver to convert the unencrypted NSData object to a NSDictionary object.

Now lets look at how we would use these two methods in our iOS code.

NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:@"Hi" forKey:@"greetings"];
[dict setObject:@"Jon" forKey:@"name"];


NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:@"encryptedFile"];

bool success = [MyCrypt saveDictionary:dict toFile:filePath withKey:@"SeCrEcTkEy"];

if (success) {
    NSDictionary *newDict = [MyCrypt getDictionaryFromFile:filePath withKey:@"SeCrEcTkEy"];
    NSLog(@"results:  %@", newDict);
   
}

We start off my creating the NSMutableDictionary object that we will be encrypting.  We then create a NSString object that represents the path to the file that will contain our encrypted NSDictionary.    We then save the encrypted NSDictionary  object using our saveDictionary:toFile:withKey: method.  If the file was successful encrypted and saved we then call our getDictionaryFromFile:withKey: method to retrieve the NSDictionary.  You will note that I put the saveDictionary:toFile:withKey: and getDictionaryFromFile:withKey: static methods in a class called MyCrypt.

If you would like to see what a NSDictionary object that is save to a file unencrypted looks like, you can add this method to your encryption class and save the NSDictionary unencrypted.

+(bool)saveDictionary:(NSDictionary *)dict toFile:(NSString *)filePath {
    NSData *dictData = [NSKeyedArchiver archivedDataWithRootObject:dict];
    return [dictData writeToFile:filePath atomically:YES];
}

One last thing for beginners to keep in mind, “hiding” your encryption key in your code does not secure it and obfuscation can only get you so far.  Therefore, encrypting your data will only get you so far.  A determined cracker, with access to your execute will eventually be able to crack your encryption.  My recommendation is to make your application secure enough so it is not worth the crackers time/effort. 

1 comment: