I’m working on an OSX interface to a web service right now. Of course, one of the requirements is for a username and password. Simple — just use keychain, right?
Well, it might be simple, apart from the fact that Keychain Services has a hoary old API. It’s C-based, and is like taking a step back in time to the early ’90s. After using the rest of the Cocoa Objective-C APIs, it’s a real shock.
// Try to find a password. const char *svc = "ABCD"; SecKeychainItemRef itemRef = nil; OSStatus st = SecKeychainFindGenericPassword(NULL, strlen(svc), svc, 0, NULL, NULL, NULL, &itemRef); if (st == noErr) { // use the keychain item } else { NSLog(@"Ooops, got error status %d", st); }
Pulling items out of a keychain is even more long winded.
UInt32 length; char *password; SecKeychainAttribute attributes[4]; SecKeychainAttributeList list; // list attributes we want to read. attributes[0].tag = kSecAccountItemAttr; attributes[1].tag = kSecDescriptionItemAttr; attributes[2].tag = kSecLabelItemAttr; attributes[3].tag = kSecModDateItemAttr; list.count = 4; list.attr = attributes; // Can pass NULL for password if not needed. OSStatus status = SecKeychainItemCopyContent(item, NULL, &list, &length, (void **)&password); if (status == noErr) { // Use the now filled-in attributes. NB: Password is NOT zero terminated, but "length" long. } else { // Complain bitterly. } SecKeychainItemFreeContent(&list, password);
So what to do? Of course, it’d be great if there were a nice, OO wrapper. I had a quick look and found EMKeyChain (on github). Here’s a sample usage:
NSString *password = [EMGenericKeychain passwordForUsername:@"dom" service:@"MyService"];
It’s a world of difference. Sadly, EMKeychain doesn’t quite support the use case I want. I don’t necessarily know the username in advance. This leaves me with a few choices:
- Give up, and just use the vanilla Keychain API. Bleurgh.
- Fix EMKeychain to support my use case.
- Use the preferences API to store the username, and then EMKeychain will work as expected.
I’m leaning quite strongly towards the third option.