Why does Keychain Services return the wrong keychain content?

Posted by Graham Lee on Stack Overflow See other posts from Stack Overflow or by Graham Lee
Published on 2010-05-12T10:38:26Z Indexed on 2010/05/12 10:44 UTC
Read the original article Hit count: 319

Filed under:
|
|

I've been trying to use persistent keychain references in an iPhone application. I found that if I created two different keychain items, I would get a different persistent reference each time (they look like 'genp.......1', 'genp.......2', …). However, attempts to look up the items by persistent reference always returned the content of the first item. Why should this be? I confirmed that my keychain-saving code was definitely creating new items in each case (rather than updating existing items), and was not getting any errors. And as I say, Keychain Services is giving a different persistent reference for each item.

I've managed to solve my immediate problem by searching for keychain items by attribute rather than persistent references, but it would be easier to use persistent references so I'd appreciate solving this problem.

Here's my code:

- (NSString *)keychainItemWithName: (NSString *)name {
    NSString *path = [GLApplicationSupportFolder()
                      stringByAppendingPathComponent: name];
    NSData *persistentRef = [NSData dataWithContentsOfFile: path];
    if (!persistentRef) {
        NSLog(@"no persistent reference for name: %@", name);
        return nil;
    }
    NSArray *refs = [NSArray arrayWithObject: persistentRef];
    //get the data
    CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL,
                                                              0,
                                                              &kCFTypeDictionaryKeyCallBacks,
                                                              &kCFTypeDictionaryValueCallBacks);
    CFDictionaryAddValue(params, kSecMatchItemList, refs);
    CFDictionaryAddValue(params, kSecClass, kSecClassGenericPassword);
    CFDictionaryAddValue(params, kSecReturnData, kCFBooleanTrue);
    CFDataRef item = NULL;
    OSStatus result = SecItemCopyMatching(params, (CFTypeRef *)&item);
    CFRelease(params);
    if (result != errSecSuccess) {
        NSLog(@"error %d retrieving keychain reference for name: %@", result, name);
        return nil;
    }
    NSString *token = [[NSString alloc] initWithData: (NSData *)item
                                            encoding: NSUTF8StringEncoding];
    CFRelease(item);
    return [token autorelease];
}

- (void)setKeychainItem: (NSString *)newToken forName: (NSString *)name {
    NSData *tokenData = [newToken dataUsingEncoding: NSUTF8StringEncoding];
    //firstly, find out whether the item already exists
    NSDictionary *searchAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                      name, kSecAttrAccount,
                                      kCFBooleanTrue, kSecReturnAttributes,
                                      nil];
    NSDictionary *foundAttrs = nil;
    OSStatus searchResult = SecItemCopyMatching((CFDictionaryRef)searchAttributes,
                                                (CFTypeRef *)&foundAttrs);
    if (noErr == searchResult) {
        NSMutableDictionary *toStore = [foundAttrs mutableCopy];
        [toStore setObject: tokenData forKey: (id)kSecValueData];
        OSStatus result = SecItemUpdate((CFDictionaryRef)foundAttrs,
                                        (CFDictionaryRef)toStore);
        if (result != errSecSuccess) {
            NSLog(@"error %d updating keychain", result);
        }
        [toStore release];
        return;
    }
    //need to create the item.
    CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL,
                                                              0,
                                                              &kCFTypeDictionaryKeyCallBacks,
                                                              &kCFTypeDictionaryValueCallBacks);
    CFDictionaryAddValue(params, kSecClass, kSecClassGenericPassword);
    CFDictionaryAddValue(params, kSecAttrAccount, name);
    CFDictionaryAddValue(params, kSecReturnPersistentRef, kCFBooleanTrue);
    CFDictionaryAddValue(params, kSecValueData, tokenData);
    NSData *persistentRef = nil;
    OSStatus result = SecItemAdd(params, (CFTypeRef *)&persistentRef);
    CFRelease(params);
    if (result != errSecSuccess) {
        NSLog(@"error %d from keychain services", result);
        return;
    }
    NSString *path = [GLApplicationSupportFolder()
                      stringByAppendingPathComponent: name];
    [persistentRef writeToFile: path atomically: NO];
    [persistentRef release];
}

© Stack Overflow or respective owner

Related posts about iphone

Related posts about security