My question concerns keychains in iOS (iPhone, iPad, ...). I think (but am not sure) that the implementation of keychains under Mac OS X raises the same question with the same answer.
iOS provides five types (classes) of keychain items. You must chose one of those five values for the key kSecClass
to determine the type:
kSecClassGenericPassword used to store a generic password
kSecClassInternetPassword used to store an internet password
kSecClassCertificate used to store a certificate
kSecClassKey used to store a kryptographic key
kSecClassIdentity used to store an identity (certificate + private key)
After long time of reading apples documentation, blogs and forum-entries, I found out that a keychain item of type kSecClassGenericPassword
gets its uniqueness from the attributes kSecAttrAccessGroup
, kSecAttrAccount
and kSecAttrService
.
If those three attributes in request 1 are the same as in request 2, then you receive the same generic password keychain item, regardless of any other attributes. If one (or two or all) of this attributes changes its value, then you get different items.
But kSecAttrService
is only available for items of type kSecClassGenericPassword
, so it can't be part of the "unique key" of an item of any other type, and there seems to be no documentation that points out clearly which attributes uniquely determine a keychain item.
The sample code in the class "KeychainItemWrapper" of "GenericKeychain" uses the attribute kSecAttrGeneric
to make an item unique, but this is a bug. The two entries in this example only are stored as two distinct entries, because their kSecAttrAccessGroup
is different (one has the access group set, the other lets it free). If you try to add a 2nd password without an access group, using Apple's KeychainItemWrapper
, you will fail.
So, please, answer my questions:
- Is it true, that the combination of
kSecAttrAccessGroup
,kSecAttrAccount
andkSecAttrService
is the "unique key" of a keychain item whose kSecClass iskSecClassGenericPassword
? - Which attributes makes a keychain item unique if its
kSecClass
is notkSecClassGenericPassword
?
The primary keys are as follows (derived from open source files from Apple, see Schema.m4, KeySchema.m4 and SecItem.cpp):
kSecClassGenericPassword
, the primary key is the combination ofkSecAttrAccount
andkSecAttrService
.kSecClassInternetPassword
, the primary key is the combination ofkSecAttrAccount
,kSecAttrSecurityDomain
,kSecAttrServer
,kSecAttrProtocol
,kSecAttrAuthenticationType
,kSecAttrPort
andkSecAttrPath
.kSecClassCertificate
, the primary key is the combination ofkSecAttrCertificateType
,kSecAttrIssuer
andkSecAttrSerialNumber
.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.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.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):
I was hitting a bug the other day (on iOS 7.1) that is related to this question. I was using
SecItemCopyMatching
to read akSecClassGenericPassword
item and it kept returningerrSecItemNotFound
(-25300) even thoughkSecAttrAccessGroup
,kSecAttrAccount
andkSecAttrService
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 usingkSecAttrAccessibleWhenUnlocked
.Of course this value is not needed in the first place for
SecItemCopyMatching
, but theOSStatus
was noterrSecParam
norerrSecBadReq
but justerrSecItemNotFound
(-25300) which made it a bit tricky to find.For
SecItemUpdate
I have experienced the same issue but in this method even using the samekSecAttrAccessible
in thequery
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.