确定是否存在Touch ID保护的钥匙串项目?

19
有没有一种方法可以在不提示用户使用Touch ID的情况下,确定是否使用Touch ID访问控制设置了iOS钥匙链中的项目(密码、令牌等)?我们需要在执行操作之前确定凭据是否已经保存到钥匙链中(具有Touch ID保护),但我们不想打断用户的Touch ID提示。 我尝试过以下方法...
NSMutableDictionary *query = ...
query[(__bridge id)kSecUseNoAuthenticationUI] = (__bridge id)kCFBooleanTrue;

OSStatus opStatus = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL);

...

然而,当调用此代码时,用户仍会看到Touch ID提示。我们不希望在UI上显示任何东西,只希望在OSStatus中返回一个错误,如果需要Touch ID,则应该如此。

有什么想法吗?

2个回答

16

基于neoneye的代码和Swift 3。我添加了errSecAuthFailed。

    query[kSecClass as String] : kSecClassGenericPassword,
    query[kSecAttrService as String] : "serviceName"    
    query[kSecUseAuthenticationUI as String] = kSecUseAuthenticationUIFail

    DispatchQueue.global().async {

        var result : AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)

        if status == errSecInteractionNotAllowed {

            DispatchQueue.main.async {

                // item exists
            }
        } else if status == errSecAuthFailed {

            DispatchQueue.main.async {

                // item exists but someone removed the touch id or passcode
            }
        } else if status == errSecItemNotFound {

            DispatchQueue.main.async {

                // it does not exist
            }
        } else {

            DispatchQueue.main.async {

                // another OSStatus
            }
        }
    }

1
不要对于为什么@neoneye的答案中的kSecUseNoAuthenticationUI:@YES被更改为kSecUseAuthenticationUI:kSecUseAuthenticationUIFail感到困惑:前者已被弃用,推荐使用后者。请参见:https://developer.apple.com/documentation/security/ksecusenoauthenticationui - Nicolas Miari
顺便说一下,如果由于尝试次数过多而被iOS锁定Touch ID,则会出现errSecAuthFailed错误(需要用户通过密码屏幕进行恢复)。 - algrid

13
NSDictionary *query = @{
                        (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                        (__bridge id)kSecAttrService: @"SampleService",
                        (__bridge id)kSecUseNoAuthenticationUI: @YES
                        };

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    CFTypeRef dataTypeRef = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)(query), &dataTypeRef);
    if (status == errSecInteractionNotAllowed) {
        NSLog(@"ITEM EXIST");
    } else if (status == errSecItemNotFound) {
        NSLog(@"ITEM DOES NOT EXIST");
    } else {
        NSLog(@"status: %@", @(status));
    }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接