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



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

它可能与 SecItemCopyMatching() 有关,但该函数的文档不是很清楚(我未能在网上找到一个像样的样本)。

SecItemCopyMatching是正确的

选择。首先,我们构建查询字典,以便在字典中返回项目的属性,并返回所有项目:

NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
    (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes,
    (__bridge id)kSecMatchLimitAll, (__bridge id)kSecMatchLimit,
    nil];

由于SecItemCopyMatching至少需要返回的 SecItem s 的类,因此我们创建一个包含所有类的数组......

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(至少找到一项)。

Swift

4 对 @Cosmin 的 Swift 3 答案的更新。

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 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
    }

其他 Swift 代码片段似乎都有点复杂。你真的不必那么多地搞乱可变指针,你可能想要有适当的错误管理。我在 Swift 中实现了我的版本,只是通过调整 Apple 文档中的代码。这是为那些使用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)
}

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

更新以在字典中包含 kSecClassIdentity 和 kSecClassCertificate 信息

我也不认为使用UnsafeMutablePointer(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
}

最新更新