Jabbering Giraffe

Keychained

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:

  1. Give up, and just use the vanilla Keychain API. Bleurgh.
  2. Fix EMKeychain to support my use case.
  3. Use the preferences API to store the username, and then EMKeychain will work as expected.

I’m leaning quite strongly towards the third option.