Search code examples
macoskeychain

SecItemCopyMatching returns empty dictionary


For some reason I got an empty dictionary when calling SecItemCopyMatching on OSX 10.8.4. The corresponding item is in the keychain and contains username and password. SecItemCopyMatching founds it (errSecSuccess) but the result dictionary just contains 0 entries. I would expect it to have at least username and password data, so what's wrong with my request?

OSStatus status;
NSMutableDictionary *query = [NSMutableDictionary dictionary];
[query setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
[query setObject:(id)kSecReturnAttributes forKey:(id)kCFBooleanTrue];
[query setObject:@"MyService" forKey:(id)kSecAttrService];

CFDictionaryRef dictRef = NULL;
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&dictRef);
if (status != errSecSuccess) {
    CFStringRef errorRef = SecCopyErrorMessageString(status, NULL);
    NSLog(@"%s: %@", __FUNCTION__, (__bridge NSString *)errorRef);
    CFRelease(errorRef);
    return nil;
}

// --> dictRef empty

if (dictRef != NULL) CFRelease(dictRef);

enter image description here


Solution

  • There is a mistake in the request. I've mixed up key and object.

    [query setObject:(id)kSecReturnAttributes forKey:(id)kCFBooleanTrue];
    

    must be

    [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
    

    however as bdash pointed out, it will return only non-encrypted attributes like the username. To get the password there is another request necessary with passing

    [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    

    So here what I have now:

    OSStatus status;
    NSMutableDictionary *query = [NSMutableDictionary dictionary];
    [query setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
    [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
    [query setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
    [query setObject:@"MyService" forKey:(id)kSecAttrService];
    
    // get username
    CFDictionaryRef dictRef = NULL;
    status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&dictRef);
    if (status != errSecSuccess) {
        CFStringRef errorRef = SecCopyErrorMessageString(status, NULL);
        NSLog(@"%s: %@", __FUNCTION__, (__bridge NSString *)errorRef);
        CFRelease(errorRef);
        return nil;
    }
    
    NSString *username = (__bridge NSString *)CFDictionaryGetValue(dictRef, kSecAttrAccount);
    CFRelease(dictRef);
    
    // get password
    [query removeObjectForKey:(id)kSecReturnAttributes];
    [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    CFDataRef dataRef = NULL;
    status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&dataRef);
    if (status != errSecSuccess) {
        CFStringRef errorRef = SecCopyErrorMessageString(status, NULL);
        NSLog(@"%s: %@", __FUNCTION__, (__bridge NSString *)errorRef);
        CFRelease(errorRef);
        return nil;
    }
    
    NSString *password = [[NSString alloc] initWithData:(__bridge NSData *)(dataRef) encoding:NSUTF8StringEncoding];
    CFRelease(dataRef);