Почему Keychain Services возвращают неверное содержимое цепочки для ключей? - PullRequest
2 голосов
/ 12 мая 2010

Я пытался использовать постоянные ссылки цепочки для ключей в приложении iPhone. Я обнаружил, что если бы я создал два разных элемента цепочки для ключей, я бы каждый раз получал разные постоянные ссылки (они выглядят как 'genp ....... 1', 'genp ....... 2',…) , Однако попытки поиска элементов по постоянной ссылке всегда возвращали содержимое первого элемента. Почему это должно быть? Я подтвердил, что мой код, сохраняющий брелок, определенно создавал новые элементы в каждом случае (а не обновлял существующие) и не получал никаких ошибок. И, как я уже сказал, Keychain Services дает разные постоянные ссылки для каждого элемента.

Мне удалось решить мою непосредственную проблему путем поиска элементов цепочки для ключей по атрибутам, а не по постоянным ссылкам, но было бы проще использовать постоянные ссылки, поэтому я был бы признателен за решение этой проблемы.

Вот мой код:

- (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];
}

1 Ответ

1 голос
/ 13 июля 2010

Оказывается, что использование kSecMatchItemList не работает вообще.

Я сделал так:

NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
                     (id)kSecClassGenericPassword, kSecClass,
                     persistentRef, (id)kSecValuePersistentRef,
                     (id)kCFBooleanTrue, kSecReturnAttributes,
                     (id)kCFBooleanTrue, kSecReturnData,
                     nil];
NSDictionary *result = nil;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query,
                                    (CFTypeRef*)&result);

, который возвратил атрибуты и данные для постоянной ссылки. Документация в заголовке о преобразовании «постоянной ссылки» в «стандартную ссылку» не имеет никакого смысла. Надеюсь, это поможет.

...