Steal This Code and Protect Their Data: Simplifying KeyChain Access

September 3, 2011    

Invalidname Meet iPhone Explorer Invalidname Learn Keychain Noel Llopis Keychain is Obtuse

 

 

##The Code

The last couple of months, I’ve been working on my first Mac App (more on that in a later post).  As part of this App, I’m calling a REST API that requires that I have the user’s password for that service to use in the API calls.  Although that API is a minor part of the App, and although the service doesn’t have horrible consequences if someone gets the user’s password for it (in my opinion at least), there was no way I was going to store that password on disk unencrypted.  After all, users have a bad tendency to use the same password for multiple services, and one of those other services might contain important information.

So I dug into the Keychain documentation, and it took me a while to figure it out.  Meanwhile, I was learning Bindings for the Mac App, since in my time programming iOS, I’d never had the chance to use Bindings before.  And I decided that it was a good opportunity for me to combine the two and learn something, and maybe help someone else along the way. I fought with it off and on for a month or so, and released it under the MIT license at the end of July.

This is the result.  It’s a project that simplifies using the Keychain by making it accessible through methods patterned after NSUserDefaults.

##Their Data

So here’s the problem, anything you persist in your App unencrypted can easily be extracted by a program like this,   If you put your own encryption in your App, you could run afoul of Apple’s encryption policies and potentially Law Enforcement Organizations.  The KeyChain makes it possible to protect the data that you persist from (all but the most determined) prying eyes.

Now many programmers don’t think they’re persisting any data that they need to protect, because they don’t get passwords from their users.  But think for a minute about other information the user might not want anyone to be able to see.  And then think about any data that you wouldn’t want the user to be able to read (or alter).  I don’t write games myself, but when I talk to my friends that do, I hear them complain a lot about people “cheating” by trying to hack their save games.  While you wouldn’t want to stick a huge amount of data in the keychain, some strategically selected pieces of data (current amount of “gold” the user has, or maximum hit points) might be appropriate to store in a safer location than in a file on disk.

##How to Use it

I intentionally wanted to write this library to be as easy to use as possible, so I decided to make it match the semantics of NSUserDefaults, since that’s in every iOS programming book I’ve ever seen, so in theory, it should be well known to anyone needing it.

To install it, check it out from github, grab the 4 files in the  PDKeychainBindingsController folder (the .h and .m files for PDKeychainBindings and  PDKeychainBindingsController) and drag them into your project in XCode.

Then, when you would normally have used:

[NSUserDefaults standardUserDefaults]

You should be able to call

[PDKeychainBindings sharedKeychainBindings]

instead (at least for the most common methods).  If you’re doing an OS X App, and you’re binding a NSTextField or the like, then where you would have called

[NSUserDefaultsController sharedUserDefaultsController]

use

[PDKeychainBindingsController sharedKeychainBindingsController]

instead (again, at least for the most common methods).

There are two differences, the first is that the Keychain API only wants to work with Strings (well, NSStrings).  So if you want to store something else in there, you need to convert it to a string yourself before you put it in the keychain (and change it back it when you take it out).

The second is that, in order to simplify it, I took out the need to run the synchronize method.  As soon as you call the set method, it gets persisted.

I’d like to thank  Chris Adamson and Noel Llopis for unwittingly helping me decide on the topic for this post.