What makes a keychain item unique (in iOS)?
The primary keys are as follows (derived from open source files from Apple, see Schema.m4, KeySchema.m4 and SecItem.cpp):
- For a keychain item of class
kSecClassGenericPassword
, the primary key is the combination ofkSecAttrAccount
andkSecAttrService
. - For a keychain item of class
kSecClassInternetPassword
, the primary key is the combination ofkSecAttrAccount
,kSecAttrSecurityDomain
,kSecAttrServer
,kSecAttrProtocol
,kSecAttrAuthenticationType
,kSecAttrPort
andkSecAttrPath
. - For a keychain item of class
kSecClassCertificate
, the primary key is the combination ofkSecAttrCertificateType
,kSecAttrIssuer
andkSecAttrSerialNumber
. - For a keychain item of class
kSecClassKey
, the primary key is the combination ofkSecAttrApplicationLabel
,kSecAttrApplicationTag
,kSecAttrKeyType
,kSecAttrKeySizeInBits
,kSecAttrEffectiveKeySize
, and the creator, start date and end date which are not exposed by SecItem yet. - For a keychain item of class
kSecClassIdentity
I haven't found info on the primary key fields in the open source files, but as an identity is the combination of a private key and a certificate, I assume the primary key is the combination of the primary key fields forkSecClassKey
andkSecClassCertificate
.
As each keychain item belongs to a keychain access group, it feels like the keychain access group (field kSecAttrAccessGroup
) is an added field to all these primary keys.
I was hitting a bug the other day (on iOS 7.1) that is related to this question. I was using SecItemCopyMatching
to read a kSecClassGenericPassword
item and it kept returning errSecItemNotFound
(-25300) even though kSecAttrAccessGroup
, kSecAttrAccount
and kSecAttrService
were all matching the item in the keychain.
Eventually I figured out that kSecAttrAccessible
didn't match. The value in the keychain held pdmn = dk (kSecAttrAccessibleAlways
), but I was using kSecAttrAccessibleWhenUnlocked
.
Of course this value is not needed in the first place for SecItemCopyMatching
, but the OSStatus
was not errSecParam
nor errSecBadReq
but just errSecItemNotFound
(-25300) which made it a bit tricky to find.
For SecItemUpdate
I have experienced the same issue but in this method even using the same kSecAttrAccessible
in the query
parameter didn't work. Only completely removing this attribute fixed it.
I hope this comment will save few precious debugging moments for some of you.
Answer given by @Tammo Freese seems to be correct (but not mentioning all primary keys). I was searching for some proof in the documentation. Finally found:
Apple Documentation mentioning primary keys for each class of secret (quote below):
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 andkSecAttrService.
- 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 bundledtogether, the primary keys are the same as for a certificate. Becausea private key may be certified more than once, the uniqueness of thecertificate determines that of the identity.