I’ve had occasion to work with Apple’s Keychain security APIs a couple times now (once while developing Cocoalicious, and now for a few iPhone projects I’m working on), and, in terms of complexity of use relative to simplicity of the task at hand, I think it has to be about the worst API I’ve ever encountered. I’m sure it’s infinitely flexible and capable of doing all kinds of crazy stuff that my non-Lone Gunman brain doesn’t even begin to comprehend, but for me (and I’m guessing 90% of application developers out there), all it really needs to do is let me store a password safely and retrieve it later.
Now, it would be one thing if Keychain lacked an intuitive API but at least provided good sample code, but to my mind much of Apple’s relevant sample code, including the GenericKeychain sample app in Apple’s iPhone Developer Program portal, is fairly obfuscated and does little to easily illuminate how a user of the API would accomplish common, concrete tasks.
To make things even more annoying, the Keychain API for the phone is different than the one on Mac OS X, and it doesn’t work in the simulator. The sample code unhelpfully sidesteps this issue with an #ifdef and and a compile-time error saying that the code can only be run on the device (which, for the uninitiated, is kind of a pain because having to always build, copy the app to the device, then run and debug it on there is a slow process).
As you can probably tell, all of this pissed me off enough that I decided there should be better Keychain code out there for the iPhone. So last night I sat down and, based on documentation, Apple sample code, and some code that Jonathan Wight had posted awhile back cobbled together what I’m hoping will be the definitive Keychain wrapper for most peoples’ purposes. It has a dead simple and hopefully self-explanatory interface, with only two methods:
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
+ (void) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error;
The first method allows you to request the password associated with an existing username for a particular service name (I’ve just been using the name of my app as a service name). The second allows you to store a username/password/service name combo, and allows you to specify whether or not the appropriate keychain item should be updated with the provided password if an existing one is found that matches the username and service name pair. The last parameter of each is a reference to an NSError object which will contain lower level error information if something goes wrong (and be nil if it does not).
The source contains two sets of #ifdef’d method implementations, one set that uses the OS X Keychain APIs for the simulator, and another that uses the iPhone Keychain APIs for the device. So far I’ve tested it in both scenarios and it seems to be working great (although be forewarned: you might get a confusingly worded dialog asking to create a new keychain the first time you run the code in the simulator—just go with it, it seems to be fine).
If anyone has anything to add or improve, by all means let me know. I posted the code on GitHub so it can hopefully evolve easily within the community. Happy Keychaining!
Update: The code I was adapting was actually by Mike Malone as part of his OAuth on the iPhone work. Apparently it was actually an adaptation of an adaptation of Jonathan Wight’s work. I’ve updated the attribution on the code accordingly.