0

Try to create and retreive a symmetric key from keychain :

Add the key

let key  = Data(repeating: 0xee, count: 32)
let name = "test"
let attributes = [
    kSecAttrKeyType: kSecAttrKeyTypeAES,
    kSecAttrKeySizeInBits: NSNumber(value: 256)
] as CFDictionary

var error: Unmanaged<CFError>?
let secKey = SecKeyCreateFromData(attributes, key as CFData, &error)

let addquery = [
    kSecClass: kSecClassKey,
    kSecAttrKeyClass: kSecAttrKeyClassSymmetric,
    kSecAttrLabel: name,
    kSecValueRef: secKey!
] as CFDictionary

let status = SecItemAdd(addquery as CFDictionary, nil)

if status != errSecSuccess {
    print(SecCopyErrorMessageString(status, nil)!)
}

The keychain item is created

Get the key

let name = "test"
let getquery = [
    kSecClass: kSecClassKey,
    kSecAttrKeyClass: kSecAttrKeyClassSymmetric,
    kSecAttrLabel: name
] as [CFString : Any]

var secKey: CFTypeRef?

let status = SecItemCopyMatching(getquery as CFDictionary, &secKey)

if status == errSecSuccess {
    if let dic = SecKeyCopyAttributes(secKey as! SecKey) as? [CFString: Any] {
        if let key = dic[kSecValueData] {
            print("Ok")
        } else {
            print("Cannot get the key")
        }
    } else {
        print("Error retrieving dictionnary")
    }
} else {
    print(SecCopyErrorMessageString(status, nil)!)
}

If the key is added and retrieve in the same run it works. The number of elements in the dic is 21.

But if i only try to get the key stored in keychain i get the dictionary but not the key. The number of elements in the dic is 20 (kSecValueData is missing).

What parameters are missing to get the key ?

Thank you

Bram
  • 2,718
  • 1
  • 22
  • 43
Stephane
  • 391
  • 1
  • 2
  • 13

1 Answers1

0

In order to retrieve your key from the KeyChain, you should also specify the kSecAttrAccessible option:

let attributes = [
    kSecAttrKeyType: kSecAttrKeyTypeAES,
    kSecAttrKeySizeInBits: NSNumber(value: 256),
    kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked
] as CFDictionary

This one can be queried and retrieved when the Mac is unlocked.

You can then use the code you already provided

let getquery = [
    kSecClass: kSecClassKey,
    kSecAttrKeyClass: kSecAttrKeyClassSymmetric,
    kSecAttrLabel: name,
    kSecMatchLimit: kSecMatchLimitAll
] as [CFString : Any]

var secKeys: CFTypeRef?
let status = SecItemCopyMatching(getquery as CFDictionary, &secKeys)

if status == errSecSuccess {
    for symmetricKey in (secKeys as! [SecKey]) {
        guard let keyAttributes = SecKeyCopyAttributes(symmetricKey) as? [CFString: Any] else {
            fatalError("No key attributes for symmetric key")
        }
        
        guard let keyData = keyAttributes[kSecValueData] as? Data else {
            fatalError("No key data for symmetric key")
        }
        print("Key data retrieved: \(keyData.base64EncodedString())")
    }
}

Now, you might encounter the old keys you've added, which still will not return any data (as their accessibility flag is set incorrectly). Remove those using the Keychain Access application on your Mac. After that, you should be able to add an AES key and retrieve an AES key.

Bram
  • 2,718
  • 1
  • 22
  • 43
  • I added kSecMatchLimit: kSecMatchLimitOne, but if i add kSecReturnData: kCFBooleanTrue! or kSecReturnAttributes: kCFBooleanTrue!, i get EXC_BAD_ACCESS in SecKeyCopyAttributes. if i remove it i get an array but without the key as i described. – Stephane Jun 20 '20 at 09:32
  • @Stephane please help me out here. I did retry my answer, and it indeed crashes. However, after reading your question more thoroughly, I do wonder what you mean with the phrase 'if I only try to get the key stored in keychain'. Your add query adds a key every time, there is no constraint on the KeyChain, when you use a label. Your `SecItemCopyMatching` function returns an array of keys. I have updated my answer with new code, that will allow to retrieve symmetric key data. – Bram Jun 22 '20 at 08:46
  • Thank you for your help. it does not work "No key data for symmetric key". If i add (create a new key) AND retrieve the key in the same function (same run) it works. But if i only try to get the key (who is stored in keychain) it does not work. i want to store the key and retrieve it later, (there is nothing special) but that does not work. I cannot create a key every time ! i need to stored it and retrieve it at will. It retrieve 20 elements of the dic instead of 21. – Stephane Jun 22 '20 at 13:03
  • Very strange. I've tested this in a test playground where it works. I don't know whether you create it with a playground too, but there are some entitlements you need when creating keys on macOS for the KeyChain. You could check that – Bram Jun 22 '20 at 13:18
  • Yes you right, with playground it works.(i just tested now) But in a simple pgm in debug mode it does not works. Yes something is missing for an application I don't know what it is. – Stephane Jun 22 '20 at 13:32
  • @Stephane I've fixed it. See the answer above. It had to do with the accessibility of the key. Remove all the old ones (as mentioned in the answer) and try again – Bram Jun 22 '20 at 14:05
  • I delete the key and add the attribute kSecAttrAccessibleWhenUnlocked but its the same. it failed again. its in debug mode without signing capabilities.(very simple). I don't know what is bypassed in playground. – Stephane Jun 22 '20 at 15:14
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/216446/discussion-between-craz1k0ek-and-stephane). – Bram Jun 22 '20 at 16:46
  • In fact by changing any files of the application trigger the loss of the key, ( changing code, info.plist, interface ...) so this problem is not solved , i put the question on apple forum and they not answered as usual so and i try to fill the form on feedback assistant and it does not work for the moment. – Stephane Jun 26 '20 at 19:50