将 SecAccessControl 与密码和生物识别一起使用时,钥匙串 SecItemAdd 失败

Posted

技术标签:

【中文标题】将 SecAccessControl 与密码和生物识别一起使用时,钥匙串 SecItemAdd 失败【英文标题】:Keychain SecItemAdd fails when using SecAccessControl with Passcode & Biometry 【发布时间】:2021-03-11 07:05:07 【问题描述】:

我目前能够重现一个错误,其中SecItemAdd 在设置了密码但尚未在设备上设置生物识别(但通常可用)的设备上进行测试时失败。

错误: OSStatus 给出-25293,好像是errSecAuthFailed

我想要实现的目标: 我想以设备密码作为最低安全要求将项目存储到钥匙串中。此外,如果启用了 Biometry,我希望允许用户使用它来保护和访问该项目。与.userPresence 相比,我想阻止在生物特征更改 时访问该项目,因此标志中的.biometryCurrentSet 似乎是正确的选择。

因此,对于保护级别,失败的组合是 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnlySecAccessControlCreateFlags[.biometryCurrentSet, .or, .devicePasscode]

示例代码(重现的最小演示):

import SwiftUI

struct ContentView: View 

    func keychainTest() 
        var attributes: [String: Any] = [kSecValueData as String: "abc".data(using: .utf8)!]
        var error: Unmanaged<CFError>?

        defer 
            error?.release()
        

        guard let accessControl = SecAccessControlCreateWithFlags(
            nil,
            kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly as CFString,
            [.biometryCurrentSet, .or, .devicePasscode], // this fails when Device has Biometry deactivated
            &error
        ) else 
            debugPrint("Error creating Access Control")
            return
        

        attributes[kSecAttrAccessControl as String] = accessControl

        // try adding new keychain item
        attributes[kSecClass as String] = kSecClassGenericPassword
        attributes[kSecAttrAccount as String] = "keychaintest.mybundle.com.keyid1234"

        let saveStatus = SecItemAdd(attributes as CFDictionary, nil)

        if saveStatus != errSecSuccess 
            debugPrint("Error Saving")
        
    

    var body: some View 
        Text("Hello, world!")
            .padding()
            .onAppear 
                self.keychainTest()
            
    


注意:最小的演示是在 SwiftUI 中创建的,但最初的失败源来自 UIKit 项目,这在此处无关紧要。 FaceID 隐私字符串在Info.plist 中设置。

只要我使用[.userPresence](相当于[.biometryAny, .or, .devicePasscode])而不是想要的标志,SecItemAdd 就会成功。

我错过了什么?

编辑:当然存在具有相同键的上一个项目。我每次尝试都会更改它并使用新的测试设备(ios 14.4)。

编辑 2:这似乎相关。 IOS not able to create privatekey if only devicecode is set on device?。不一样(这里是SecItemAdd),但访问控制也创建了一个有效的引用,但后来 Key 方法失败了。

另外还转储了 saveStatus failed 分支中的属性:

(lldb) po attributes
▿ 4 elements
  ▿ 0 : 2 elements
    - key : "v_Data"
    ▿ value : 3 bytes
      - count : 3
      ▿ pointer : 0x000000016b282d50
        - pointerValue : 6092762448
      ▿ bytes : 3 elements
        - 0 : 97
        - 1 : 98
        - 2 : 99
  ▿ 1 : 2 elements
    - key : "accc"
    - value : <SecAccessControlRef: akpu;od(pkofn(1)cup(true)cbio(pbioc()pbioh()));odel(true);oe(true)>
  ▿ 2 : 2 elements
    - key : "acct"
    - value : "keychaintest.mybundle.com.keyid1234.13"
  ▿ 3 : 2 elements
    - key : "class"
    - value : genp

【问题讨论】:

【参考方案1】:

归档为雷达 FB9039075。

一位队友(https://***.com/users/2451589/julien-klindt,谢谢!)之后收到了来自 Apple(使用 DTS)的反馈:

这里的.or操作符指的是读操作。也就是说,要读取项目,系统必须能够满足一个或多个指定的约束。但是,在您的情况下,在 add 操作中事情失败了。这里.biometryCurrentSet 的存在表明钥匙串必须用当前的生物特征集标记该项目,这不可能工作,因为当前没有配置生物特征。

所以这是设计的。如果需要,Apple 建议为所描述的行为创建功能请求。

就解决方法而言,捕获此错误然后询问本地身份验证是否启用了生物识别似乎是合理的。如果不是,您可以退回到.devicePasscode

如果不需要对生物特征进行更改使项目无效,我建议改用.userPresence

【讨论】:

以上是关于将 SecAccessControl 与密码和生物识别一起使用时,钥匙串 SecItemAdd 失败的主要内容,如果未能解决你的问题,请参考以下文章

实战 | 将 Android 生物识别身份验证整合至应用中

该应用程序可以使用 Firebase 进行生物识别登录吗?

剑桥量子计算与中美冠科生物合作,利用量子机器学习发现新的抗癌药物

生物网络,RNA 与疾病关联分析

东京大学和积水房屋启动生物多样性和健康联合研究

指纹识别预处理