热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

升级到iOS13后,钥匙串查询始终返回errSecItemNotFound

如何解决《升级到iOS13后,钥匙串查询始终返回errSecItemNotFound》经验,请帮忙看看怎么搞?

我将密码存储到iOS钥匙串中,以后再检索它们以在我的应用程序中实现“记住我”(自动登录)功能。

我围绕Security.framework功能(SecItemCopyMatching(),等等)实现了自己的包装器,直到iOS 12为止,它的运行都像是一种魅力。

现在,我正在测试我的应用程序不会与即将推出的iOS 13兼容,瞧瞧:

SecItemCopyMatching() 总是回来 .errSecItemNotFound

...即使我以前已经存储了要查询的数据。

我的包装是用静态特性方便地提供的价值类kSecAttrServicekSecAttrAccount组装查询字典时:

class LocalCredentialStore {

    private static let serviceName: String = {
        guard let name = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String else {
            return "Unknown App"
        }
        return name
    }()
    private static let accountName = "Login Password" 

// ...

使用以下代码密码插入钥匙串:

/* 
  - NOTE: protectWithPasscode is currently always FALSE, so the password
  can later be retrieved programmatically, i.e. without user interaction. 
 */
static func storePassword(_ password: String, protectWithPasscode: Bool, completion: (() -> Void)? = nil, failure: ((Error) -> Void)? = nil) {
    // Encode payload:
    guard let dataToStore = password.data(using: .utf8) else {
        failure?(NSError(localizedDescription: ""))
        return
    }

    // DELETE any previous entry:
    self.deleteStoredPassword()

    // INSERT new value: 
    let protection: CFTypeRef = protectWithPasscode ? kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly : kSecAttrAccessibleWhenUnlocked
    let flags: SecAccessCOntrolCreateFlags= protectWithPasscode ? .userPresence : []

    guard let accessCOntrol= SecAccessControlCreateWithFlags(
        kCFAllocatorDefault,
        protection,
        flags,
        nil) else {
            failure?(NSError(localizedDescription: ""))
            return
    }

    let insertQuery: NSDictiOnary= [
        kSecClass: kSecClassGenericPassword,
        kSecAttrAccessControl: accessControl,
        kSecValueData: dataToStore,
        kSecUseAuthenticationUI: kSecUseAuthenticationUIAllow,
        kSecAttrService: serviceName, // These two values identify the entry;
        kSecAttrAccount: accountName  // together they become the primary key in the Database.
    ]
    let resultCode = SecItemAdd(insertQuery as CFDictionary, nil)

    guard resultCode == errSecSuccess else {
        failure?(NSError(localizedDescription: ""))
        return
    }
    completion?()
}

...然后,我通过以下方式检索密码:

static func loadPassword(completion: @escaping ((String?) -> Void)) {

    // [1] Perform search on background thread:
    DispatchQueue.global().async {
        let selectQuery: NSDictiOnary= [
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: serviceName,
            kSecAttrAccount: accountName,
            kSecReturnData: true,
            kSecUseOperationPrompt: "Please authenticate"
        ]
        var extractedData: CFTypeRef?
        let result = SecItemCopyMatching(selectQuery, &extractedData)

        // [2] Rendez-vous with the caller on the main thread:
        DispatchQueue.main.async {
            switch result {
            case errSecSuccess:
                guard let data = extractedData as? Data, let password = String(data: data, encoding: .utf8) else {
                    return completion(nil)
                }
                completion(password) // 

(我不认为我用来打任何电话的词典中的任何条目都具有不合适的价值……但也许直到现在我仍遗漏了一些只是为了“获得通过”的东西)

我已经建立了一个可以正常工作的项目(Xcode 11 beta)来说明问题的存储库。

密码存储总是成功;密码加载:

成功在Xcode中10 - 12的iOS(或更早),但

.errSecItemNotFound在Xcode 11-iOS 13上无法使用。


更新:我无法在设备上重现该问题,只能在模拟器上重现。在设备上,成功检索到存储的密码。也许这是针对x86平台的iOS 13 Simulator和/或iOS 13 SDK上的错误或限制。

更新2:如果有人想出了一种解决问题的替代方法(无论是通过设计还是通过Apple的监督),我将接受它作为答案。


推荐阅读
author-avatar
紫木之阁_229
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有