Keychain:项目报告为errSecItemNotFound,但在添加时接收errSecDuplicateItem [英] Keychain: Item reported as errSecItemNotFound, but receive errSecDuplicateItem on addition

查看:619
本文介绍了Keychain:项目报告为errSecItemNotFound,但在添加时接收errSecDuplicateItem的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题一直困扰着我,我希望有人能够深入了解原因。从本质上讲,我有一个小百分比的用户无法将项目保存/更新到钥匙串。有问题的控制流程如下:

This issue has been bugging me for a while, and I hope someone has insight as to the cause of this. Essentially, I have a small percentage of users who are unable to save/update items to the keychain. The problematic flow of control is as follows:


  1. 我们使用 SecItemCopyMatching检查项目是否存在。这将返回错误代码 errSecItemNotFound

  1. We check for the existence of the item using SecItemCopyMatching. This returns the error code errSecItemNotFound

然后我们尝试通过 SecItemAdd ,但这会返回 errSecDuplicateItem

We then try to add the item via SecItemAdd, but this then returns errSecDuplicateItem.

因此,我们有一些用户无法更新钥匙串项的子集,要求他们恢复设备以清除钥匙串。这显然是一种不可接受的解决方法。它似乎以前对它们起作用,但现在已进入这个不可更新的循环。

Because of this, we have some users who are unable to update a subset of keychain items at all, requiring them to restore their device to clear the keychain. This is obviously an unacceptable workaround. It seemed to work for them before, but have now got into this non-updatable cycle.

经过研究,我看到了有关<$中使用的搜索查询的问题c $ c> SecItemCopyMatching 不够具体,但我的代码尽可能使用常见的搜索查询。

After researching, I've seen issues regarding the search query used in SecItemCopyMatching not being specific enough, but my code uses a common search query wherever possible.

+ (NSMutableDictionary*)queryForUser:(NSString*)user key:(NSString*)key
{
    if (!key || !user) { return nil; }

    NSString* bundleId = [[NSBundle mainBundle] bundleIdentifier];
    NSString* prefixedKey = [NSString stringWithFormat:@"%@.%@", bundleId, key];

    NSMutableDictionary* query = [NSMutableDictionary dictionary];
    [query addEntriesFromDictionary:@{(__bridge id)kSecClass          : (__bridge id)kSecClassGenericPassword}];
    [query addEntriesFromDictionary:@{(__bridge id)kSecAttrAccount    : user}];
    [query addEntriesFromDictionary:@{(__bridge id)kSecAttrService    : prefixedKey}];
    [query addEntriesFromDictionary:@{(__bridge id)kSecAttrLabel      : prefixedKey}];
    [query addEntriesFromDictionary:@{(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly}];

    return query;
}

执行更新/添加的代码如下(对于详细程度而言遗憾):

The code to do the updating/adding is as follows (sorry for the verbosity):

// Setup the search query, to return the *attributes* of the found item (for use in SecItemUpdate)
NSMutableDictionary* query = [self queryForUser:username key:key];
[query addEntriesFromDictionary:@{(__bridge id)kSecReturnAttributes : (__bridge id)kCFBooleanTrue}];

// Prep the dictionary we'll use to update/add the new value
NSDictionary* updateValues = @{(__bridge id) kSecValueData : [value dataUsingEncoding:NSUTF8StringEncoding]};

// Copy what we (may) already have
CFDictionaryRef resultData = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef*)&resultData);

// If it already exists, update it
if (status == noErr) {
    // Create a new query with the found attributes
    NSMutableDictionary* updateQuery = [NSMutableDictionary dictionaryWithDictionary:(__bridge NSDictionary*)resultData];
    [updateQuery addEntriesFromDictionary:@{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword}];

    // Update the item in the keychain
    status = SecItemUpdate((__bridge CFDictionaryRef)updateQuery, (__bridge CFDictionaryRef)updateValues);

    if (status != noErr) {
        // Update failed, I've not seen this case occur as of yet
    }
}
else {
    // Add the value we want as part of our original search query, and add it to the keychain
    [query addEntriesFromDictionary:updateValues];
    [query removeObjectForKey:(__bridge id)kSecReturnAttributes];
    status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);

    if (status != noErr) {
        // Addition failed, this is where I'm seeing errSecDuplicateItem
    }
}

我们尝试使用 SecItemDelete 而不是检查/更新,但这也返回 errSecItemNotFound SecItemAdd 直接失败。删除代码是:

We tried using SecItemDelete instead of checking/updating, but this also returned errSecItemNotFound with SecItemAdd failing straight after. The delete code is:

+ (BOOL)deleteItemForUser:(NSString *)username withKey:(NSString *)itemKey {
    if (!username || !itemKey) { return NO; }

    NSString * bundleId = [[NSBundle mainBundle] bundleIdentifier];
    NSString * prefixedItemKey = [NSString stringWithFormat:@"%@.%@", bundleId, itemKey];

    NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)kSecClassGenericPassword, kSecClass,
                           username, kSecAttrAccount,
                           prefixedItemKey, kSecAttrService, nil];

    OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query);

    if (status != noErr) {
        // Failed deletion, returning errSecItemNotFound
    }

    return (status == noErr);
}

虽然我们为应用程序定义了2个钥匙串访问组,但受影响的钥匙串项目确实如此没有将访问组分配为属性(通过文档,这意味着将对所有访问组进行搜索)。我还没有看到除 errSecItemNotFound errSecDuplicateItem 以外的任何其他错误代码。

Whilst we have defined 2 keychain access groups for the application, affected keychain items do not have an access group assigned as an attribute (which by the documentation, means searching will be done for all access groups). I've yet to see any other error code other than errSecItemNotFound and errSecDuplicateItem.

只有少数用户遇到这种情况这一事实让我感到困惑。关于可能导致此问题的钥匙串,关于多线程,刷新,后台访问等,是否还需要考虑其他因素?

The fact that only a small set of users get into this condition really confuses me. Are there any other considerations I need to take into account regarding the keychain that could be causing this, regarding multithreading, flushing, background access etc…?

非常感谢。我宁愿坚持使用Keychain Services API而不是使用第三方库。我想了解这里的根本问题。

Help much appreciated. I'd rather stick with using the Keychain Services API instead of using a 3rd party library. I'd like to understand the fundamental problem here.

推荐答案

kSecClassGenericPassword的唯一键由;

kSecAttrAccount
kSecAttrService

要检查其存在,请仅使用这些属性(包括 kSecReturnAttributes 标志)查询钥匙串存储。

To check for its existence, query the keychain store with only these attributes (including kSecReturnAttributes flag).

包括 kSecAttrLabel kSecAttrAccessible 将排除任何现有项目使用相同的唯一键,但具有不同的属性。

Including kSecAttrLabel and kSecAttrAccessible will exclude any existing item with the same unique key, but with different attributes.

确认其(非)存在后,添加其他属性并添加或更新。

Once you have confirmed its (non)existence, add the additional attributes and Add or Update.

这篇关于Keychain:项目报告为errSecItemNotFound,但在添加时接收errSecDuplicateItem的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆