升级到iOS 13后,钥匙串查询始终返回errSecItemNotFound [英] Keychain Query Always Returns errSecItemNotFound After Upgrading to iOS 13
问题描述
我将密码存储到iOS钥匙串中,然后检索它们以在我的应用程序中实现记住我"(自动登录)功能.
I am storing passwords into the iOS keychain and later retrieving them to implement a "remember me" (auto-login) feature on my app.
我围绕Security.framework
函数(SecItemCopyMatching()
等)实现了自己的包装器,直到iOS 12为止,它的运行一直很吸引人.
I implemented my own wrapper around the Security.framework
functions (SecItemCopyMatching()
, etc.), and it was working like a charm up until iOS 12.
现在,我正在测试我的应用不会随着即将推出的iOS 13中断,瞧瞧:
Now I am testing that my app doesn't break with the upcoming iOS 13, and lo and behold:
SecItemCopyMatching()
始终返回.errSecItemNotFound
SecItemCopyMatching()
always returns .errSecItemNotFound
...即使我以前已经存储了要查询的数据.
...even though I have previously stored the data I am querying.
我的包装器是一个具有静态属性的类,可在组装查询字典时方便地提供kSecAttrService
和kSecAttrAccount
的值:
My wrapper is a class with static properties to conveniently provide the values of the kSecAttrService
and kSecAttrAccount
when assembling the query dictionaries:
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"
// ...
我正在使用以下代码将密码插入到钥匙串中:
I am inserting the password into the keychain with code like the following:
/*
- 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?()
}
...然后,我要使用以下方式检索密码:
...and later, I am retrieving the password with:
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) // < SUCCESS
case errSecUserCanceled:
completion(nil)
case errSecAuthFailed:
completion(nil)
case errSecItemNotFound:
completion(nil)
default:
completion(nil)
}
}
}
}
(我不认为我用来打任何电话的词典中的任何条目都具有不合适的价值……但也许直到现在我仍遗漏了一些只是为了获得通过"的东西)
我已经建立了存储库,其中包含一个正在运行的项目(Xcode 11 beta)演示问题.
I have set up a repository with a working project (Xcode 11 beta) that demonstrates the problem.
密码存储总是成功;密码加载:
The password storing always succeeds; The password loading:
-
在Xcode 10-iOS 12(及更低版本)上,
- 成功,但
- 失败,在Xcode 11-iOS 13上出现
.errSecItemNotFound
失败.
- Succeeds on Xcode 10 - iOS 12 (and earlier), but
- Fails with
.errSecItemNotFound
on Xcode 11 - iOS 13.
更新:我无法在设备上重现该问题,只能在Simulator上重现.在设备上,成功检索到存储的密码. 也许这是针对x86平台的iOS 13 Simulator和/或iOS 13 SDK上的错误或限制.
UPDATE: I can not reproduce the issue on the device, only Simulator. On the device, the stored password is retrieved successfully. Perhaps this is a bug or limitation on the iOS 13 Simulator and/or iOS 13 SDK for the x86 platform.
更新2:如果有人想出了一种解决该问题的替代方法(无论是通过设计还是通过Apple的监督),我将接受它作为答案.
UPDATE 2: If someone comes up with an alternative approach that somehow works around the issue (whether by design or by taking advantage of some oversight by Apple), I will accept it as an answer.
推荐答案
我遇到了类似的问题,我在模拟器上通过任何与钥匙串相关的操作都得到了errSecItemNotFound
,但仅.在真实的设备上,它是完美的,我已经在不同的模拟器上测试了最新的Xcode(beta,GM,稳定版),而给我带来麻烦的是iOS 13.
I've had a similar issue where I was getting errSecItemNotFound
with any Keychain-related action but only on a simulator. On real device it was perfect, I've tested with latest Xcodes (beta, GM, stable) on different simulators and the ones that were giving me a hard time were iOS 13 ones.
问题是我在查询属性kSecClass
中使用了kSecClassKey
,但是没有'required'值(请参阅哪些类与哪些值
The problem was that I was using kSecClassKey
in query attribute kSecClass
, but without the 'required' values (see what classes go with which values here) for generating a primary key:
-
kSecAttrApplicationLabel
-
kSecAttrApplicationTag
-
kSecAttrKeyType
-
kSecAttrKeySizeInBits
-
kSecAttrEffectiveKeySize
kSecAttrApplicationLabel
kSecAttrApplicationTag
kSecAttrKeyType
kSecAttrKeySizeInBits
kSecAttrEffectiveKeySize
有用的是为kSecClass
选择kSecClassGenericPassword
并提供了生成主键的必需"值:
And what helped was to pick kSecClassGenericPassword
for kSecClass
and provide the 'required' values for generating a primary key:
-
kSecAttrAccount
-
kSecAttrService
kSecAttrAccount
kSecAttrService
请参阅此处有关kSecClass类型以及有关内容的更多信息其他属性应该与它们一起使用.
See here on more about kSecClass types and what other attributes should go with them.
通过得出一个结论,我开始了一个新的iOS 13项目,并复制了应用程序中使用的钥匙串包装程序,但效果并不理想,因此我找到了有关使用钥匙串的可爱指南
I came to this conclusion by starting a new iOS 13 project and copying over the Keychain wrapper that was used in our app, as expected that did not work so I've found this lovely guide on using keychain here and tried out their wrapper which no surprise worked, and then went line by line comparing my implementation with theirs.
此问题已在雷达中报告: http://openradar.appspot.com/7251207
This issue already reported in radar: http://openradar.appspot.com/7251207
希望这会有所帮助.
这篇关于升级到iOS 13后,钥匙串查询始终返回errSecItemNotFound的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!