What makes a keychain item unique (in iOS)?

我的问题与 iOS (iPhone,iPad,...)中的钥匙链有关。我认为(但不确定)在 MacOSX 下键链的实现提出了同样的问题和相同的答案。


IOS 提供了五种类型(类)的密钥链项。您必须为键 kSecClass选择这五个值中的一个,以确定类型:

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.

但是 kSecAttrService只适用于 kSecClassGenericPassword类型的项目,因此它不能成为任何其他类型项目的“唯一键”的一部分,而且似乎没有文档清楚地指出哪些属性是唯一决定密钥链项目的。

“ GenericKeychain”的类“ KeychainItemWrapper”中的示例代码使用属性 kSecAttrGeneric使项目唯一,但这是一个 bug。这个示例中的两个条目只存储为两个不同的条目,因为它们的 kSecAttrAccessGroup是不同的(一个有访问组集,另一个让它自由)。如果你尝试在没有访问组的情况下使用苹果的 KeychainItemWrapper添加第二个密码,你将会失败。

所以,请回答我的问题:

  • kSecAttrAccessGroupkSecAttrAccountkSecAttrService的组合是一个 kSecClass 为 kSecClassGenericPassword的密钥链项目的“唯一密钥”,这是真的吗?
  • 如果密钥链项目的 kSecClass不是 kSecClassGenericPassword,那么哪些属性使其成为唯一的?
26457 次浏览

主键如下(源自 Apple 的开源文件,参见 架构 m4KeySchema.m4SecItem.cpp) :

  • For a keychain item of class kSecClassGenericPassword, the primary key is the combination of kSecAttrAccountkSecAttrService
  • 对于 kSecClassInternetPassword类的钥匙链项目,主键是 kSecAttrAccountkSecAttrSecurityDomainkSecAttrServerkSecAttrProtocolkSecAttrAuthenticationTypekSecAttrPortkSecAttrPath的组合。
  • 对于类 kSecClassCertificate的密钥链项,主键是 kSecAttrCertificateTypekSecAttrIssuerkSecAttrSerialNumber的组合。
  • 对于 kSecClassKey类的钥匙链项目,主键是 kSecAttrApplicationLabelkSecAttrApplicationTagkSecAttrKeyTypekSecAttrApplicationLabelkSecAttrApplicationTagkSecAttrKeyTypekSecAttrApplicationTagkSecAttrApplicationTagkSecAttrKeyTypekSecAttrApplicationTagkSecAttrKeyTypekSecClassKeykSecClassKeykSecClassKeykSecClassKeykSecClassKeykSecAttrApplicationLabelkSecAttrKeyType、 AB 还没有被 SecItem 公开的 kSecAttrKeySizeInBitskSecAttrEffectiveKeySize和创建者、开始日期和结束日期。
  • 对于类 kSecClassIdentity的密钥链条目,我没有在开源文件中找到关于主密钥字段的信息,但是鉴于身份是私钥和证书的组合,我假设主密钥是 kSecClassKeykSecClassCertificate的主密钥字段的组合。

由于每个密钥链项都属于一个密钥链访问组,因此感觉上密钥链访问组(字段 kSecAttrAccessGroup)是所有这些主键的一个添加字段。

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.

最后我发现 kSecAttrAccessible不匹配。Keychain 中的值包含 pdmn = dk (kSecAttrAccessibleAlways) ,但我使用的是 kSecAttrAccessibleWhenUnlocked

当然这个值在 SecItemCopyMatching中是不需要的,但是 OSStatus既不是 errSecParam也不是 errSecBadReq,而只是 errSecItemNotFound(- 25300) ,这使得它有点难找。

对于 SecItemUpdate,我也遇到过同样的问题,但是在这个方法中,即使在 query参数中使用相同的 kSecAttrAccessible,也不起作用。只有完全删除此属性才能修复它。

我希望这个评论可以为您节省一些宝贵的调试时间。

@ Tammo Freese 给出的答案似乎是正确的(但没有提到所有的主键)。我在文件里找证据。最终发现:

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 一些属性在所有类中都是通用的。特别是,在适用的情况下,kSecAttrSynchronizer 和 kSecAttrAccessGroup 是主键集的一部分. The additional per-class primary keys are listed below:

  • 对于通用密码,主键 < strong > 包括 kSecAttrAccount 和 KSecAttrService.
  • 对于互联网密码,主键 < strong > 包括 kSecAttrAccount, 安全域,安全服务器,安全协议, kSecAttrAuthenticationType, kSecAttrPort, and kSecAttrPath.
  • 对于证书,主键 < strong > 包括 kSecAttr證 icateType, KSecAttrIssuer 和 kSecAttrSerialNumber.
  • 对于键项,主键 < strong > 包括 kSecAttrKeyClass, kSecAttrKeyType, kSecAttrApplicationLabel, kSecAttrApplicationTag, KSecAttrKeySizeInBits,和 kSecAttrEffectioneKeySize.
  • 对于标识项,它们是绑定的证书和私钥 together, the primary keys are 与证书相同。 Because 一个私钥可能会被认证多次,但是 证书确定身份的证书。

下面是另一条关于密钥链条目唯一性的有用信息,可以在 这个苹果文档页面的“确保可搜索性”部分找到。

为了以后能够找到这个项目,您将使用您对其属性的了解。在此示例中,服务器和帐户是该项的区别特征。对于常量属性(这里是服务器) ,在查找期间使用相同的值。相反,account 属性是动态的,因为它保存用户在运行时提供的值。只要你的应用程序从不添加具有不同属性的类似项目(比如同一服务器上不同账户的密码) ,你就可以省略这些动态属性作为搜索参数,而是与项目一起检索它们。因此,当您查找密码时,也会得到相应的用户名。

If your app does add items with varying dynamic attributes, you’ll need a way to choose among them during retrieval. One option is to record information about the items in another way. For example, if you keep records of users in a Core Data model, you store the username there after using keychain services to store the password field. Later, you use the user name pulled from your data model to condition the search for the password.

在其他情况下,通过添加更多属性来进一步描述该项可能是有意义的。例如,您可以在原始的添加查询中包含 kSecAttrLabel属性,提供一个字符串,该字符串为特定用途标记该项。然后您就可以使用这个属性来缩小以后的搜索范围。

Item of class kSecClassInternetPassword was used in the example, but there is a note that says:

Keychain 服务还提供相关的 kSecClassGenericPassword 项类。通用密码在大多数方面与 Internet 密码相似,但它们缺乏特定于远程访问的特定属性(例如,它们没有 kSecAttrServer 属性)。如果不需要这些额外的属性,可以使用通用密码。