Перечислите все элементы цепочки для ключей в моем приложении iOS

Какой самый простой способ программно (из моего приложения) получить все элементы, хранящиеся в цепочке для ключей?

Вероятно, это как-то связано с SecItemCopyMatching(), но документация для этой функции не очень ясна (и мне не удалось найти достойный образец в Интернете).

4 ответа

Решение

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

В рабочем коде вы должны проверить, что OSStatus вернулся SecItemCopyMatching либо errSecItemNotFound (ничего не найдено) или errSecSuccess (хотя бы один предмет был найден).

Обновление Swift 4 до ответа @Cosmin

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 кажутся немного запутанными. Вам действительно не нужно так много возиться с MutablePointers, и вы, вероятно, захотите иметь правильное управление ошибками. Я реализовал свою версию в 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)
}

Версия Swift 3 с xcode 9.1

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 в словарь.

Я также не думаю, что вызов 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
}
Другие вопросы по тегам