46

What's the easiest way to programmatically (from within my app) get all items stored in the keychain?

It probably has something to do with SecItemCopyMatching(), but the documentation for that function is not very clear (and I failed to find a decent sample on the web).

noamtm
  • 12,435
  • 15
  • 71
  • 107

6 Answers6

59

SecItemCopyMatching is the right call for that. First we build our query dictionary so that the items' attributes are returned in dictionaries, and that all items are returned:

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

As SecItemCopyMatching requires at least the class of the returned SecItems, we create an array with all the classes…

NSArray *secItemClasses = [NSArray arrayWithObjects:
                           (__bridge id)kSecClassGenericPassword,
                           (__bridge id)kSecClassInternetPassword,
                           (__bridge id)kSecClassCertificate,
                           (__bridge id)kSecClassKey,
                           (__bridge id)kSecClassIdentity,
                           nil];

...and for each class, set the class in our query, call SecItemCopyMatching, and log the result.

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

In production code, you should check that the OSStatus returned by SecItemCopyMatching is either errSecItemNotFound (no items found) or errSecSuccess (at least one item was found).

Sam Spencer
  • 8,492
  • 12
  • 76
  • 133
Tammo Freese
  • 10,514
  • 2
  • 35
  • 44
  • Thanks! Haven't tested it yet, but looks like the right answer. – noamtm Jul 31 '12 at 12:47
  • I got all null in print out both in iPhone and simulator. Is there something more I should do? – karim Apr 12 '13 at 10:43
  • Check the return value of `SecItemCopyMatching`. If it is `errSecItemNotFound`, you haven't stored any keychain items in your iOS app, you won't get any back. – Tammo Freese Apr 12 '13 at 11:09
  • I tried adding embeddedCertificate(single certificate containing multiple ones) but it just returns only one entry. Is there a way out or 'SecItemAdd' is not adding them properly ? Any ideas. – RandomGuy Jul 20 '15 at 18:10
  • In case someone was searching for this to check what is the access control level of elements in the keychain then the above code prints it. You will see something like this: "" in printed logs and that "cku" here describes the access control level. In this example it means kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly which you can check in xcode debbuger: (lldb) po kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly prints "cku". – Leszek Szary Feb 11 '22 at 11:52
14

A Swift 4 update to @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
}
Sam Spencer
  • 8,492
  • 12
  • 76
  • 133
uplearned.com
  • 3,393
  • 5
  • 44
  • 59
6

Swift 3+ version that returns also the keys (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
    }
Jonny
  • 15,955
  • 18
  • 111
  • 232
Cosmin
  • 201
  • 2
  • 8
  • Using Swift 3 on Xcode 9.2 this is crashing at `let key : String = item[kSecAttrAccount] as! String` with `Could not cast value of type '__NSCFData' (0x109b30348) to 'NSString' (0x1069030d0).`. I'm not sure why. – Jay Whitsitt Dec 15 '17 at 20:59
  • 1
    Changing the lines that set key and value to this made the crash not happen, although it eliminates several objects that I'm not sure how to pull out: `if let key = item[kSecAttrAccount] as? String, let value = item[kSecValueData] as? Data {` – Jay Whitsitt Dec 15 '17 at 21:35
5

The other Swift code snippets all seem a bit convoluted. You don't really have to mess around the MutablePointers that much, and you probably want to have proper error management. I implemented my version in Swift just by tweaking the code in the Apple documentation. Here it is in for those using 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)
}
thenewpotato
  • 101
  • 1
  • 9
4

Swift 3 version with 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
}

Can be called like :

debugPrint(getAllKeyChainItemsOfClass(kSecClassGenericPassword as String))
Jonny
  • 15,955
  • 18
  • 111
  • 232
ergunkocak
  • 3,334
  • 1
  • 32
  • 31
1

Updated to include kSecClassIdentity and kSecClassCertificate information in dictionary

I also don't think calling withUnsafeMutablePointer(to:_:) is necessary.

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
}
Christopher Graf
  • 1,929
  • 1
  • 17
  • 34