首页 文章

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

提问于
浏览
1

我使用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行,我将获得它在keychain中存在的状态 .

2 回答

  • 1

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

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

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

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

  • 2

    它看起来像一个Apple bug,我前段时间打开了rdar:// 24237713 .

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

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

相关问题