I would like to save within an account marta@domain.com two values. Example of the dictionaries:
kSecAttrAccount = marta@domain.com
kSecAttrGeneric = value1Key
kSecValueData = value1
kSecAttrAccount = marta@domain.com
kSecAttrGeneric = value2Key
kSecValueData = value2
Is it possible? I'm getting an error to do the query when I try to keep the second value.
I attach a snippet of my code:
func addCredentials(_ credentials: KeychainCredentials) throws {
let account = credentials.account
let password = credentials.password.data(using: String.Encoding.utf8)!
let generic = credentials.generic
let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
.userPresence,
nil)
// Build the query
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: account,
kSecAttrGeneric as String: generic,
kSecAttrAccessControl as String: accessControl as Any,
kSecValueData as String: password]
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else { throw KeychainError(status: status) }
}
For kSecClassGenericPassword
, the combination of service and account must be unique. For the list of which attributes are part of the primary key, see the documentation for errSecDuplicateItem.
The system considers an item to be a duplicate for a given keychain when that keychain already has an item of the same class with the same set of composite primary keys. Each class of keychain item has a different set of primary keys, although a few attributes are used in common across all classes. In particular, where applicable, kSecAttrSynchronizable and kSecAttrAccessGroup are part of the set of primary keys. The additional per-class primary keys are listed below:
For generic passwords, the primary keys include kSecAttrAccount and kSecAttrService.
For internet passwords, the primary keys include kSecAttrAccount, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrProtocol, kSecAttrAuthenticationType, kSecAttrPort, and kSecAttrPath.
For certificates, the primary keys include kSecAttrCertificateType, kSecAttrIssuer, and kSecAttrSerialNumber.
For key items, the primary keys include kSecAttrKeyClass, kSecAttrKeyType, kSecAttrApplicationLabel, kSecAttrApplicationTag, kSecAttrKeySizeInBits, and kSecAttrEffectiveKeySize.
For identity items, which are a certificate and a private key bundled together, the primary keys are the same as for a certificate. Because a private key may be certified more than once, the uniqueness of the certificate determines that of the identity.
If I were building this, I would likely put all the key/values into a dictionary, and serialize that to a single kSecAttrAccount record.
Alternately, you could merge your account and service values together into the account (if you have a service), and put value1Key
in the service attribute. Then account+service would be unique to a given key.