禁用并重新启用Touch ID时,钥匙串错误-25300(errSecItemNotFound)

时间:2015-09-22 16:15:05

标签: ios iphone keychain touch-id

我使用Touch ID标志将密码保存到钥匙串:

+ (void)setPasscode:(NSString *)passcode
{
    CFErrorRef error = NULL;
    SecAccessControlRef sacObject;

    sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
                                                    kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
                                                    kSecAccessControlUserPresence, &error);


    if(sacObject == NULL || error != NULL)
    {
        DLog(@"can't create sacObject: %@", error);
        return;
    }

    NSDictionary *attributes = @{
                                 (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                                 (__bridge id)kSecAttrService: kKeychainServiceName,
                                 (__bridge id)kSecValueData: [passcode dataUsingEncoding:NSUTF8StringEncoding],
                                 (__bridge id)kSecUseNoAuthenticationUI: @YES,
                                 (__bridge id)kSecAttrAccessControl: (__bridge_transfer id)sacObject
                                 };

    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        SecItemAdd((__bridge CFDictionaryRef)attributes, nil);
    });
}

检索它:

+ (void)getCurrentPasscodeWithSuccess:(void (^)(NSString *))success failure:(void (^)(OSStatus))failure
{
    NSDictionary *query = @{
                            (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecAttrService: kKeychainServiceName,
                            (__bridge id)kSecReturnData: @YES,
                            (__bridge id)kSecUseOperationPrompt: kOperationPrompt
                            };

    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        CFTypeRef dataTypeRef = NULL;

        OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)(query), &dataTypeRef);
        if (status == errSecSuccess)
        {
            if (success) {
                NSData *resultData = ( __bridge_transfer NSData *)dataTypeRef;
                NSString *result = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
                success(result);
            }
        } else {
            if (failure) {
                failure(status);
            }
        }
    });
}

这很好用。但是,如果禁用Touch ID,并在设备上重新启用它,SecItemCopyMatching将返回OSStatus -25300(errSecItemNotFound)。 问题是该项目仍然存在(我认为)。因为当我尝试访问它时,会出现Touch ID提示。

我尝试使用以下方法检查项目是否存在:

+ (void)checkIfPasscodeExistsInKeychainWithCompletion:(void (^)(BOOL))completion
{
    NSDictionary *query = @{

                            (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecAttrService: kKeychainServiceName,
                            };

    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        CFTypeRef dataTypeRef = NULL;

        OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)(query), &dataTypeRef);
        BOOL exists = status != errSecItemNotFound;

        if (completion) {
            completion(exists);
        }
    });
}

这会触发Touch ID提示,然后返回在提供触摸时不存在的错误。

但如果我删除(__bridge id)kSecClass:(__ bridge id)kSecClassGenericPassword行,我会得到它在钥匙串中的状态。

2 个答案:

答案 0 :(得分:2)

您正在使用 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly 作为商品的Accessiblity常量。这意味着只有在设备上设置了密码时才能添加项目,并添加了TouchID。如果您将项目保存在钥匙串中后删除了设备密码(或在您的情况下为TouchID),则此项目将不再可用。

即使您重新添加设备密码(或TouchID),此项目也不再可用。您必须创建同一项目的副本。

有关kSecAttrAccessible常量的更多信息,请参阅文档:Keychain Item Accessibility Constants

有关整个过程如何运作的信息,请参阅Apple's security whitepaper

答案 1 :(得分:1)

它看起来像是一个苹果虫,我不久前打开了rdar:// 24237713。

使用包含kSecMatchLimitAll以外的匹配限制的查询的SecItemCopyMatching将在设备密码打开后返回错误的结果&关闭。似乎不再可访问的旧项目不会从内部存储中删除,而只会标记为不可用,但它们仍然参与创建结果集。

测试项目以证明问题: https://github.com/mndgs/TestKeychainBug