【发布时间】:2012-06-13 13:59:10
【问题描述】:
以编程方式(从我的应用程序中)获取存储在钥匙串中的所有项目的最简单方法是什么?
它可能与 SecItemCopyMatching() 有关,但该函数的文档不是很清楚(我未能在网络上找到合适的示例)。
【问题讨论】:
以编程方式(从我的应用程序中)获取存储在钥匙串中的所有项目的最简单方法是什么?
它可能与 SecItemCopyMatching() 有关,但该函数的文档不是很清楚(我未能在网络上找到合适的示例)。
【问题讨论】:
SecItemCopyMatching 是正确的选择。首先,我们构建查询字典,以便在字典中返回项目的属性,并返回所有项目:
NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes,
(__bridge id)kSecMatchLimitAll, (__bridge id)kSecMatchLimit,
nil];
由于SecItemCopyMatching 至少需要返回的SecItems 的类,我们创建一个包含所有类的数组...
NSArray *secItemClasses = [NSArray arrayWithObjects:
(__bridge id)kSecClassGenericPassword,
(__bridge id)kSecClassInternetPassword,
(__bridge id)kSecClassCertificate,
(__bridge id)kSecClassKey,
(__bridge id)kSecClassIdentity,
nil];
...对于每个类,在我们的查询中设置类,调用SecItemCopyMatching,并记录结果。
for (id secItemClass in secItemClasses) {
[query setObject:secItemClass forKey:(__bridge id)kSecClass];
CFTypeRef result = NULL;
SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
NSLog(@"%@", (__bridge id)result);
if (result != NULL) CFRelease(result);
}
在生产代码中,您应该检查SecItemCopyMatching 返回的OSStatus 是errSecItemNotFound(未找到项)还是errSecSuccess(至少找到一项)。
【讨论】:
SecItemCopyMatching的返回值。如果是errSecItemNotFound,你的iOS应用中没有存储任何钥匙串项目,你将不会得到任何回报。
Swift 4 更新为 @Cosmin's Swift 3 answer。
open func getAllKeyChainItemsOfClass(_ secClass: String) -> [String:String] {
let query: [String: Any] = [
kSecClass as String : secClass,
kSecReturnData as String : kCFBooleanTrue,
kSecReturnAttributes as String : kCFBooleanTrue,
kSecReturnRef as String : kCFBooleanTrue,
kSecMatchLimit as String: kSecMatchLimitAll
]
var result: AnyObject?
let lastResultCode = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
var values = [String:String]()
if lastResultCode == noErr {
let array = result as? Array<Dictionary<String, Any>>
for item in array! {
if let key = item[kSecAttrAccount as String] as? String,
let value = item[kSecValueData as String] as? Data {
values[key] = String(data: value, encoding:.utf8)
}
}
}
return values
}
【讨论】:
其他 Swift 代码 sn-ps 似乎都有些复杂。您实际上不必过多地处理 MutablePointers,并且您可能希望进行适当的错误管理。我只是通过调整Apple documentation 中的代码在 Swift 中实现了我的版本。这是为那些使用 Xcode 11 的人准备的。
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword, // change the kSecClass for your needs
kSecMatchLimit as String: kSecMatchLimitAll,
kSecReturnAttributes as String: true,
kSecReturnRef as String: true]
var items_ref: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &items_ref)
guard status != errSecItemNotFound else { throw KeychainError.noPassword }
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }
let items = items_ref as! Array<Dictionary<String, Any>>
// Now loop over the items and do something with each item
for item in items {
// Sample code: prints the account name
print(item[kSecAttrAccount as String] as? String)
}
【讨论】:
更新为在字典中包含 kSecClassIdentity 和 kSecClassCertificate 信息
我也不认为调用 withUnsafeMutablePointer(to:_:) 是必要的。
func getAllKeyChainItemsOfClass(_ secClass: String) -> [String:AnyObject] {
let query: [String: Any] = [
kSecClass as String : secClass,
kSecReturnData as String : true,
kSecReturnAttributes as String : true,
kSecReturnRef as String : true,
kSecMatchLimit as String: kSecMatchLimitAll
]
var result: AnyObject?
let lastResultCode = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
// this also works, although I am not sure if it is as save as calling withUnsafeMutabePointer
// let lastResultCode = SecItemCopyMatching(query as CFDictionary, &result)
var values = [String: AnyObject]()
if lastResultCode == noErr {
let array = result as? Array<Dictionary<String, Any>>
for item in array! {
if let key = item[kSecAttrAccount as String] as? String,
let value = item[kSecValueData as String] as? Data {
values[key] = String(data: value, encoding:.utf8) as AnyObject?
}
// including identities and certificates in dictionary
else if let key = item[kSecAttrLabel as String] as? String,
let value = item[kSecValueRef as String] {
values[key] = value as AnyObject
}
}
}
return values
}
【讨论】:
带有 xcode 9.1 的 Swift 3 版本
func getAllKeyChainItemsOfClass(_ secClass: String) -> [String:String] {
let query: [String: Any] = [
kSecClass as String : secClass,
kSecReturnData as String : kCFBooleanTrue,
kSecReturnAttributes as String : kCFBooleanTrue,
kSecReturnRef as String : kCFBooleanTrue,
kSecMatchLimit as String : kSecMatchLimitAll
]
var result: AnyObject?
let lastResultCode = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
var values = [String:String]()
if lastResultCode == noErr {
let array = result as? Array<Dictionary<String, Any>>
for item in array! {
if let key = item[kSecAttrAccount as String] as? String,
let value = item[kSecValueData as String] as? Data {
values[key] = String(data: value, encoding:.utf8)
}
}
}
return values
}
可以这样称呼:
debugPrint(getAllKeyChainItemsOfClass(kSecClassGenericPassword as String))
【讨论】:
还返回键的 Swift 3+ 版本 (kSecAttrAccount):
open func getAllKeyChainItemsOfClass(_ secClass: String) -> [String:String] {
let query: [String: Any] = [
kSecClass : secClass,
kSecReturnData : kCFBooleanTrue,
kSecReturnAttributes : kCFBooleanTrue,
kSecReturnRef : kCFBooleanTrue,
kSecMatchLimit : kSecMatchLimitAll
]
var result: AnyObject?
let lastResultCode = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
var values = [String:String]()
if lastResultCode == noErr {
let array = result as? Array<Dictionary<String, Any>>
for item in array! {
if let key = item[kSecAttrAccount] as? String,
let value = item[kSecValueData] as? Data {
values[key] = String(data: value, encoding:.utf8)
}
}
}
return values
}
【讨论】:
let key : String = item[kSecAttrAccount] as! String 和 Could not cast value of type '__NSCFData' (0x109b30348) to 'NSString' (0x1069030d0). 处崩溃。我不知道为什么。
if let key = item[kSecAttrAccount] as? String, let value = item[kSecValueData] as? Data {