为什么Keychain Services会返回错误的钥匙串内容?

时间:2010-05-12 10:38:26

标签: iphone security keychain

我一直在尝试在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 个答案:

答案 0 :(得分:1)

事实证明,使用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);

返回持久性引用的属性和数据。关于将“持久性引用”转换为“标准引用”的标题中的文档根本没有意义。希望这会有所帮助。