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.
Great Post. Helped a lot. Thank you.
ReplyDelete