Calculating hashes in iOS
I intended to follow up my last post (Implementing preferences in iOS – the wrong way) with a treatise on how to implement preferences the right way. Even promised to do it. But first a brief detour.
I wanted to write a quick demo app to show how to use NSUserDefaults to implement simple preferences. For that demo, how about something that calculates the MD5 hash of an NSString? We’ll call it Hasher. Should be quick and easy, and I need the tech for the project I’m currently working on. Did a little bit of searching and discovered that Apple provides a C library called Common Crypto.
But I’d rather code in pure Objective C. A little more searching led me to Matt Gallagher’s excellent Cocoa with Love website and his HashValue wrapper class for Common Crypto. Just the thing! After a bit of confusion in using that class, which was written in 2009, I searched just a bit more and found Calvin Cestari’s slight updates that were last published in March 2013. Download his code here.
Matt’s website does a fairly good job describing his code, but there’s nothing about using it. So let’s put it together and make it run, shall we?
I started out by creating a new iPhone-only single-view application called Hasher. For this demo, there’s no need to make an iPad version.
The storyboard is simple: two labels and two text fields.
In HasherViewController.h, I hooked the TextFields up:
@interface HasherViewController : UIViewController @property (weak, nonatomic) IBOutlet UITextField *plaintext; @property (weak, nonatomic) IBOutlet UITextField *md5Hash; @end
And also hooked the plaintext field up to HasherViewController.m:
- (IBAction)calculateHash:(UITextField *)sender { }
Next, I unzipped the CCToolkit-master.zip file…
…and dragged the two files into my project. The first time, I forgot to check the “Add to Targets” box. Don’t follow my bad example there.
The API is pretty simple:
+ (HashValue*)MD5HashWithData:(NSData*)data; + (HashValue*)SHA256HashWithData:(NSData*)data; - (id)initWithBuffer:(const void*)buffer hashValueType:(enum HashValueType)aType; - (id)initMD5HashWithBytes:(const void*)bytes length:(NSUInteger)length; - (id)initSHA256HashWithBytes:(const void*)bytes length:(NSUInteger)length;
In fact, we only need the two class methods:
MD5HashWithData:data
and
SHA256HashWithData:data
For this example, we’ll only use the MD5 version. If you’re planning on hashing passwords for real, though, please note that MD5 is considered to be broken. Use SHA256. I didn’t use it here because the 64-character hashes are too long to fit in my Text Field.
So we need just three lines of code. First, we need to convert our NSString* to NSData*:
NSData* data = [self.plaintext.text dataUsingEncoding:NSUTF8StringEncoding];
Next, we simply make the call:
HashValue *hashValueMD5 = [HashValue MD5HashWithData:data];
And finally, we populate the Text Field:
self.md5Hash.text = hashValueMD5.description;
Before these lines, though, a tiny bit of housekeeping:
if ([self.plaintext.text isEqualToString:@""]) { self.md5Hash.text = @""; return; }
I discovered that, after generating a hash and deleting the plaintext, a hash is still created. So the housekeeping lines above are just for appearance’s sake.
So here’s the entire HasherViewController.m file:
// // HasherViewController.m // Hasher // // Created by Michael Morrow on 7/27/13. // Copyright (c) 2013 Business Casual Software LLC. All rights reserved. // // Permission is given to use this source code file, free of charge, in any // project, commercial or otherwise, entirely at your risk, with the condition // that any redistribution (in part or whole) of source code must retain // this copyright and permission notice. Attribution in compiled projects is // appreciated but not required. // #import "HasherViewController.h" #import "HashValue.h" @interface HasherViewController () @end @implementation HasherViewController - (IBAction)calculateHash:(UITextField *)sender { // Blank out the results when the string goes away if ([self.plaintext.text isEqualToString:@""]) { self.md5Hash.text = @""; return; } // Get and display an MD5 HashValue object NSData* data = [self.plaintext.text dataUsingEncoding:NSUTF8StringEncoding]; HashValue *hashValueMD5 = [HashValue MD5HashWithData:data]; self.md5Hash.text = hashValueMD5.description; } @end
It’s that easy! Oh, one more thing. We need to be sure to import the Security framework and the libCommonCrypto library:
At least I think I needed to import libCommonCrypto. Maybe I’ll check that out later.
Next time, I’ll expand this project to include preferences.
Download the entire xcode project for this demo here.