I am using the below code to save a private key (needed for end-to-end encryption) into the keychain. If I take out the line with kSecAttrSynchronizable as String: kCFBooleanTrue!,
then the key is correctly being saved. Seems like this line is causing the error. I am trying to get the private key to sync across all iCloud devices. I’d appreciate any help resolving this.
func save(userUID: String, privatekey: String) {
let passwordData = privatekey.data(using: .utf8)!
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrSynchronizable as String: kCFBooleanTrue!,
kSecAttrService as String: "hustles/(userUID)",
kSecAttrAccount as String: userUID,
kSecValueData as String: passwordData
]
SecItemAdd(query as CFDictionary, nil)
}
2
Answers
The solution is to also update the query in the read function by also adding in "kSecAttrSynchronizable as String: kCFBooleanTrue!,". The save query must match the read query.
First, it is better to specify the
kSecAttrAccessible
key to indicates when the keychain item is accessible, likekSecAttrAccessibleWhenUnlocked
mentioned here.And make sure the
kSecAttrService
,kSecAttrAccount
, andkSecAttrSynchronizable
combined must be unique, as explained here.That being said, the presence of
kSecAttrSynchronizable
should not inherently cause a saving operation to fail. Typically, the best approach in these situations is to check the status returned bySecItemAdd
(or other Keychain API calls) to get detailed error information.As noted in ahmed‘s answer, if you are saving a keychain item with specific attributes (like
kSecAttrSynchronizable
set tokCFBooleanTrue
), then when you try to read (or query) that item, you must also specify the same attributes to successfully retrieve it. The attributes used in the save operation essentially set the "conditions" for that keychain item, and when you query for it, you need to match those conditions.So, if you are saving a keychain item with
kSecAttrSynchronizable
set totrue
, your read/query function should also includekSecAttrSynchronizable
set totrue
.If your save function is:
Then your read function must be (inspired from this answer):
The
kSecMatchLimit as String: kSecMatchLimitOne
makes sure only one item is returned (which makes sense for our use-case, since each UID should only have one private key associated with it).That ensures your search criteria in the read function matches the attributes set when the item was saved, so you can successfully retrieve it from the keychain.