我有一个应用程序,该应用程序将用户的密码存储在设备的钥匙串中,并且可以使用设备的生物识别信息(面部ID或触摸ID)进行访问。
我成功做到了以下几点:
const SecAccessControlRef accessControl = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlUserPresence, &accessControlError);
LAContext * const localAuthContext = [[LAContext alloc] init];
NSDictionary * const addQuery = @{
(__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
(__bridge NSString *)kSecAttrAccount: username,
(__bridge NSString *)kSecAttrAccessControl: (__bridge id)accessControl,
(__bridge NSString *)kSecUseAuthenticationContext: localAuthContext,
(__bridge NSString *)kSecValueData: passwordData
};
const OSStatus addStatus = SecItemAdd((__bridge CFDictionaryRef)addQuery, nil);
当我想更新密码时出现问题。我需要使用SecItemUpdate(...)
函数。因此,我实施了一项检查,以查看给定用户名是否已存在该项目,但是由于该项目的存储方式,iPhone X上出现了Face ID提示。
NSDictionary * const findQuery = @{
(__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
(__bridge NSString *)kSecAttrAccount: username,
(__bridge NSString *)kSecReturnData: @(NO)
};
const OSStatus readStatus = SecItemCopyMatching((__bridge CFDictionaryRef)findQuery, nil);
是否有任何方法可以在不调用生物识别功能的情况下进行此操作?如果没有,如何可靠地检查我是否要添加或更新钥匙串物品?
答案 0 :(得分:0)
我无法找到一种可靠的方法来使用SecItemUpdate(...)
而无需调用生物识别技术,所以我所求助的是删除一个先前存在的条目,然后添加一个新条目(有效地对其进行更新)。
此代码段添加了一个新项目,或替换了一个现有项目。
NSData * const passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
NSString * const itemClass = (__bridge NSString *)kSecClassGenericPassword;
NSString * const account = username;
NSString * const service = [[NSBundle mainBundle] bundleIdentifier];
// First, try to find an existing item
NSDictionary * const findQuery = @{
(__bridge NSString *)kSecClass: itemClass,
(__bridge NSString *)kSecAttrService: service,
(__bridge NSString *)kSecAttrAccount: account,
(__bridge NSString *)kSecUseAuthenticationUI: (__bridge NSString *)kSecUseAuthenticationUIFail,
};
const OSStatus readStatus = SecItemCopyMatching((__bridge CFDictionaryRef)findQuery, nil);
NSLog (@"Tried to find existing secure local password. Status = %d", readStatus);
const BOOL passwordAlreadyExists = (readStatus == errSecInteractionNotAllowed);
if (passwordAlreadyExists) {
// Delete
NSDictionary * const deleteQuery = @{
(__bridge NSString *)kSecClass: itemClass,
(__bridge NSString *)kSecAttrService: service,
(__bridge NSString *)kSecAttrAccount: account,
(__bridge NSString *)kSecReturnData: @(NO)
};
const OSStatus deleteStatus = SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
NSLog (@"Deleted existing secure local password. Status = %d", deleteStatus);
}
// Create an access control instance that dictates how the item can be read later.
CFErrorRef accessControlError = nil;
const SecAccessControlRef accessControl = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlUserPresence, &accessControlError);
if (accessControlError) {
NSError * const error = (__bridge NSError *)accessControlError;
NSLog (@"There was an error creating the access control: %@", error.localizedDescription);
return;
}
// Create the context
LAContext * const localAuthContext = [[LAContext alloc] init];
localAuthContext.localizedFallbackTitle = @"Use Magic Link";
// Build the query
NSDictionary * const addQuery = @{
(__bridge NSString *)kSecClass: itemClass,
(__bridge NSString *)kSecAttrService: service,
(__bridge NSString *)kSecAttrAccount: account,
(__bridge NSString *)kSecAttrAccessControl: (__bridge id)accessControl,
(__bridge NSString *)kSecUseAuthenticationContext: localAuthContext,
(__bridge NSString *)kSecValueData: passwordData
};
// Execute
const OSStatus addStatus = SecItemAdd((__bridge CFDictionaryRef)addQuery, nil);
NSLog (@"Created secure local password. Status = %d", addStatus);
答案 1 :(得分:0)
我正在使用以下内容...
func has(service: String, account: String, options: [Options] = []) -> Bool {
var query = self.query(service: service, account: account, options: options)
query[kSecUseAuthenticationContext as String] = internalContext
var secItemResult: CFTypeRef?
status = SecItemCopyMatching(query as CFDictionary, &secItemResult)
return status == errSecSuccess || status == errSecInteractionNotAllowed
}
private var internalContext: LAContext? = {
let context = LAContext()
context.interactionNotAllowed = true
return context
}()
以前,您也可以使用UseAuthenticationUIFail,但是从iOS 14开始不推荐使用。