枚举我的 iOS 应用程序中的所有钥匙串项

Posted

技术标签:

【中文标题】枚举我的 iOS 应用程序中的所有钥匙串项【英文标题】:Enumerate all Keychain items in my iOS application 【发布时间】:2012-06-13 13:59:10 【问题描述】:

以编程方式(从我的应用程序中)获取存储在钥匙串中的所有项目的最简单方法是什么?

它可能与 SecItemCopyMatching() 有关,但该函数的文档不是很清楚(我未能在网络上找到合适的示例)。

【问题讨论】:

【参考方案1】:

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 返回的OSStatuserrSecItemNotFound(未找到项)还是errSecSuccess(至少找到一项)。

【讨论】:

谢谢!尚未测试,但看起来是正确的答案。 我在 iPhone 和模拟器中都打印出了所有的空值。还有什么我应该做的吗? 检查SecItemCopyMatching的返回值。如果是errSecItemNotFound,你的ios应用中没有存储任何钥匙串项目,你将不会得到任何回报。 我尝试添加嵌入式证书(包含多个证书的单个证书),但它只返回一个条目。有没有出路或“SecItemAdd”没有正确添加它们?任何想法。【参考方案2】:

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

【讨论】:

【参考方案3】:

其他 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)

【讨论】:

【参考方案4】:

更新为在字典中包含 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

【讨论】:

【参考方案5】:

带有 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))

【讨论】:

【参考方案6】:

还返回键的 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
    

【讨论】:

在 Xcode 9.2 上使用 Swift 3 这在 let key : String = item[kSecAttrAccount] as! StringCould not cast value of type '__NSCFData' (0x109b30348) to 'NSString' (0x1069030d0). 处崩溃。我不知道为什么。 更改设置键和值的行使崩溃不会发生,尽管它消除了几个我不知道如何拔出的对象:if let key = item[kSecAttrAccount] as? String, let value = item[kSecValueData] as? Data

以上是关于枚举我的 iOS 应用程序中的所有钥匙串项的主要内容,如果未能解决你的问题,请参考以下文章

卸载应用程序时删除钥匙串项

NSInternalInconsistencyException:无法更新钥匙串项

苹果密码钥匙串在哪里

在钥匙串中存储凭据不仅加密密码

未设置访问组时共享的钥匙串项目

许多应用程序在 iO 中使用一个商店