将数据保存在钥匙串中,只有 Swift 中的 Touch ID 才能访问 3
Save data in Keychain only accessible with Touch ID in Swift 3
我正在编写应该执行以下操作的代码:
- 在钥匙串中存储一些数据。
- 仅当用户使用 Touch ID 或密码进行身份验证时才获取数据。
我观看了 Keychain and Authentication with Touch ID 演示并了解以下内容:
If you set the right parameter while adding a new value in to Keychain, next time
you'll try to get it out, the system will automatically show the
Touch ID popup.
我写了一些代码,但我的假设不成立。这是我写的:
//
// Secret value to store
//
let valueData = "The Top Secret Message V1".data(using: .utf8)!;
//
// Create the Access Controll object telling how the new value
// should be stored. Force Touch ID by the system on Read.
//
let sacObject =
SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
.userPresence,
nil);
//
// Create the Key Value array, that holds the query to store
// our data
//
let insert_query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccessControl: sacObject!,
kSecValueData: valueData,
kSecUseAuthenticationUI: kSecUseAuthenticationUIAllow,
// This two valuse ideifieis the entry, together they become the
// primary key in the Database
kSecAttrService: "app_name",
kSecAttrAccount: "first_name"
];
//
// Execute the query to add our data to Keychain
//
let resultCode = SecItemAdd(insert_query as CFDictionary, nil);
起初我以为模拟器有问题但没有,我可以使用以下代码检查 Touch ID 是否存在:
//
// Check if the device the code is running on is capapble of
// finger printing.
//
let dose_it_can = LAContext()
.canEvaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics, error: nil);
if(dose_it_can)
{
print("Yes it can");
}
else
{
print("No it can't");
}
而且我还能够使用以下代码以编程方式显示 Touch ID 弹出窗口:
//
// Show the Touch ID dialog to check if we can get a print from
// the user
//
LAContext().evaluatePolicy(
LAPolicy.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "Such important reason ;)",
reply: {
(status: Bool, evaluationError: Error?) -> Void in
if(status)
{
print("OK");
}
else
{
print("Not OK");
}
});
总结一下
Touch ID 有效,但将值保存到钥匙串中并带有标志以由系统本身强制执行 Touch ID 无效 - 我错过了什么?
苹果示例
Apple 提供的名为 KeychainTouchID: Using Touch ID with Keychain and LocalAuthentication 的示例也显示了不一致的结果,系统未强制执行 Touch ID。
技术规格
- Xcode 8.1
- Swift 3
Touch ID 弹出窗口仅在您调用 SecItemCopyMatching()
时出现
在后台队列中。
这在 PDF 演示文稿的第 118 页上指出
Keychain and Authentication with Touch ID:
Reading a Secret
...
dispatch_async(dispatch_get_global_queue(...), ^(void){
CFTypeRef dataTypeRef = NULL;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query,
&dataTypeRef);
});
否则你会阻塞主线程,弹出窗口不会
出现。 SecItemCopyMatching()
然后失败(超时后)
错误代码 -25293 = errSecAuthFailed
.
失败在您的示例项目中不会立即显现出来,因为
它在错误情况下打印错误的变量,例如
if(status != noErr)
{
print("SELECT Error: \(resultCode)."); // <-- Should be `status`
}
更新和删除也类似。
这是包含必要的示例代码的组合版本
分派到后台队列以检索钥匙串项。
(当然 UI 更新必须分派回主队列。)
它在我的 iPhone 带有 Touch ID 的测试中按预期工作:
出现 Touch ID 弹出窗口,钥匙串项目仅在
认证成功。
Touch ID 身份验证不能在iOS模拟器上工作。
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// This two values identify the entry, together they become the
// primary key in the database
let myAttrService = "app_name"
let myAttrAccount = "first_name"
// DELETE keychain item (if present from previous run)
let delete_query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: myAttrService,
kSecAttrAccount: myAttrAccount,
kSecReturnData: false
]
let delete_status = SecItemDelete(delete_query)
if delete_status == errSecSuccess {
print("Deleted successfully.")
} else if delete_status == errSecItemNotFound {
print("Nothing to delete.")
} else {
print("DELETE Error: \(delete_status).")
}
// INSERT keychain item
let valueData = "The Top Secret Message V1".data(using: .utf8)!
let sacObject =
SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.userPresence,
nil)!
let insert_query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccessControl: sacObject,
kSecValueData: valueData,
kSecUseAuthenticationUI: kSecUseAuthenticationUIAllow,
kSecAttrService: myAttrService,
kSecAttrAccount: myAttrAccount
]
let insert_status = SecItemAdd(insert_query as CFDictionary, nil)
if insert_status == errSecSuccess {
print("Inserted successfully.")
} else {
print("INSERT Error: \(insert_status).")
}
DispatchQueue.global().async {
// RETRIEVE keychain item
let select_query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: myAttrService,
kSecAttrAccount: myAttrAccount,
kSecReturnData: true,
kSecUseOperationPrompt: "Authenticate to access secret message"
]
var extractedData: CFTypeRef?
let select_status = SecItemCopyMatching(select_query, &extractedData)
if select_status == errSecSuccess {
if let retrievedData = extractedData as? Data,
let secretMessage = String(data: retrievedData, encoding: .utf8) {
print("Secret message: \(secretMessage)")
// UI updates must be dispatched back to the main thread.
DispatchQueue.main.async {
self.messageLabel.text = secretMessage
}
} else {
print("Invalid data")
}
} else if select_status == errSecUserCanceled {
print("User canceled the operation.")
} else {
print("SELECT Error: \(select_status).")
}
}
}
我正在编写应该执行以下操作的代码:
- 在钥匙串中存储一些数据。
- 仅当用户使用 Touch ID 或密码进行身份验证时才获取数据。
我观看了 Keychain and Authentication with Touch ID 演示并了解以下内容:
If you set the right parameter while adding a new value in to Keychain, next time you'll try to get it out, the system will automatically show the Touch ID popup.
我写了一些代码,但我的假设不成立。这是我写的:
//
// Secret value to store
//
let valueData = "The Top Secret Message V1".data(using: .utf8)!;
//
// Create the Access Controll object telling how the new value
// should be stored. Force Touch ID by the system on Read.
//
let sacObject =
SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
.userPresence,
nil);
//
// Create the Key Value array, that holds the query to store
// our data
//
let insert_query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccessControl: sacObject!,
kSecValueData: valueData,
kSecUseAuthenticationUI: kSecUseAuthenticationUIAllow,
// This two valuse ideifieis the entry, together they become the
// primary key in the Database
kSecAttrService: "app_name",
kSecAttrAccount: "first_name"
];
//
// Execute the query to add our data to Keychain
//
let resultCode = SecItemAdd(insert_query as CFDictionary, nil);
起初我以为模拟器有问题但没有,我可以使用以下代码检查 Touch ID 是否存在:
//
// Check if the device the code is running on is capapble of
// finger printing.
//
let dose_it_can = LAContext()
.canEvaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics, error: nil);
if(dose_it_can)
{
print("Yes it can");
}
else
{
print("No it can't");
}
而且我还能够使用以下代码以编程方式显示 Touch ID 弹出窗口:
//
// Show the Touch ID dialog to check if we can get a print from
// the user
//
LAContext().evaluatePolicy(
LAPolicy.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "Such important reason ;)",
reply: {
(status: Bool, evaluationError: Error?) -> Void in
if(status)
{
print("OK");
}
else
{
print("Not OK");
}
});
总结一下
Touch ID 有效,但将值保存到钥匙串中并带有标志以由系统本身强制执行 Touch ID 无效 - 我错过了什么?
苹果示例
Apple 提供的名为 KeychainTouchID: Using Touch ID with Keychain and LocalAuthentication 的示例也显示了不一致的结果,系统未强制执行 Touch ID。
技术规格
- Xcode 8.1
- Swift 3
Touch ID 弹出窗口仅在您调用 SecItemCopyMatching()
时出现
在后台队列中。
这在 PDF 演示文稿的第 118 页上指出
Keychain and Authentication with Touch ID:
Reading a Secret
...dispatch_async(dispatch_get_global_queue(...), ^(void){ CFTypeRef dataTypeRef = NULL; OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, &dataTypeRef); });
否则你会阻塞主线程,弹出窗口不会
出现。 SecItemCopyMatching()
然后失败(超时后)
错误代码 -25293 = errSecAuthFailed
.
失败在您的示例项目中不会立即显现出来,因为 它在错误情况下打印错误的变量,例如
if(status != noErr)
{
print("SELECT Error: \(resultCode)."); // <-- Should be `status`
}
更新和删除也类似。
这是包含必要的示例代码的组合版本 分派到后台队列以检索钥匙串项。 (当然 UI 更新必须分派回主队列。)
它在我的 iPhone 带有 Touch ID 的测试中按预期工作: 出现 Touch ID 弹出窗口,钥匙串项目仅在 认证成功。
Touch ID 身份验证不能在iOS模拟器上工作。
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// This two values identify the entry, together they become the
// primary key in the database
let myAttrService = "app_name"
let myAttrAccount = "first_name"
// DELETE keychain item (if present from previous run)
let delete_query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: myAttrService,
kSecAttrAccount: myAttrAccount,
kSecReturnData: false
]
let delete_status = SecItemDelete(delete_query)
if delete_status == errSecSuccess {
print("Deleted successfully.")
} else if delete_status == errSecItemNotFound {
print("Nothing to delete.")
} else {
print("DELETE Error: \(delete_status).")
}
// INSERT keychain item
let valueData = "The Top Secret Message V1".data(using: .utf8)!
let sacObject =
SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.userPresence,
nil)!
let insert_query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccessControl: sacObject,
kSecValueData: valueData,
kSecUseAuthenticationUI: kSecUseAuthenticationUIAllow,
kSecAttrService: myAttrService,
kSecAttrAccount: myAttrAccount
]
let insert_status = SecItemAdd(insert_query as CFDictionary, nil)
if insert_status == errSecSuccess {
print("Inserted successfully.")
} else {
print("INSERT Error: \(insert_status).")
}
DispatchQueue.global().async {
// RETRIEVE keychain item
let select_query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: myAttrService,
kSecAttrAccount: myAttrAccount,
kSecReturnData: true,
kSecUseOperationPrompt: "Authenticate to access secret message"
]
var extractedData: CFTypeRef?
let select_status = SecItemCopyMatching(select_query, &extractedData)
if select_status == errSecSuccess {
if let retrievedData = extractedData as? Data,
let secretMessage = String(data: retrievedData, encoding: .utf8) {
print("Secret message: \(secretMessage)")
// UI updates must be dispatched back to the main thread.
DispatchQueue.main.async {
self.messageLabel.text = secretMessage
}
} else {
print("Invalid data")
}
} else if select_status == errSecUserCanceled {
print("User canceled the operation.")
} else {
print("SELECT Error: \(select_status).")
}
}
}