116

我的问题涉及 iOS 中的钥匙串(iPhone、iPad、...)。我认为(但不确定)在 Mac OS X 下实现钥匙串会引发相同的问题和相同的答案。


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)

经过长时间阅读苹果文档、博客和论坛条目后,我发现 keychain 类型的项kSecClassGenericPassword从属性中获取其唯一性kSecAttrAccessGroupkSecAttrAccount并且kSecAttrService.

如果请求 1 中的这三个属性与请求 2 中的相同,那么无论任何其他属性如何,您都会收到相同的通用密码钥匙串项。如果此属性中的一个(或两个或全部)更改其值,那么您将获得不同的项目。

kSecAttrService仅适用于 type 的项目kSecClassGenericPassword,因此它不能成为任何其他类型项目的“唯一键”的一部分,并且似乎没有文档清楚地指出哪些属性唯一地确定了钥匙串项目。

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

所以,请回答我的问题:

  • 是真的吗kSecAttrAccessGroupkSecAttrAccount和的组合kSecAttrService是 kSecClass 的钥匙串项的“唯一键”kSecClassGenericPassword吗?
  • kSecClass如果不是,哪些属性使钥匙串项目唯一kSecClassGenericPassword
4

4 回答 4

201

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

  • 对于类的钥匙串项kSecClassGenericPassword,主键是 和 的 kSecAttrAccount组合kSecAttrService
  • 对于类的钥匙串项kSecClassInternetPassword,主键是kSecAttrAccount, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrProtocol, kSecAttrAuthenticationType,kSecAttrPort和的组合kSecAttrPath
  • 对于类的钥匙串项,kSecClassCertificate主键是 和 的组合。kSecAttrCertificateTypekSecAttrIssuerkSecAttrSerialNumber
  • 对于 类 的钥匙串项kSecClassKey,主键是kSecAttrApplicationLabel, kSecAttrApplicationTag, kSecAttrKeyType, kSecAttrKeySizeInBits,kSecAttrEffectiveKeySize和创建者、开始日期和结束日期的组合,这些组合还没有被 SecItem 公开。
  • 对于类的钥匙串项,kSecClassIdentity我没有在开源文件中找到有关主键字段的信息,但由于身份是私钥和证书的组合,我假设主键是主键的组合kSecClassKey和的字段kSecClassCertificate

由于每个钥匙串项都属于一个钥匙串访问组,所以感觉钥匙串访问组(字段kSecAttrAccessGroup)是所有这些主键的附加字段。

于 2012-07-26T15:00:14.953 回答
10

前几天我遇到了一个与这个问题相关的错误(在 iOS 7.1 上)。我SecItemCopyMatching用来读取一个kSecClassGenericPassword项目并且它一直返回errSecItemNotFound(-25300)kSecAttrAccessGroupkSecAttrAccount并且kSecAttrService都与钥匙串中的项目匹配。

最终我发现这kSecAttrAccessible不匹配。钥匙串中的值持有 pdmn = dk ( kSecAttrAccessibleAlways),但我使用的是kSecAttrAccessibleWhenUnlocked.

当然,这个值一开始就不需要SecItemCopyMatching,但OSStatus不是errSecParam也不是errSecBadReq,只是errSecItemNotFound(-25300) 这使得找到它有点棘手。

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

我希望这条评论能为你们中的一些人节省一些宝贵的调试时间。

于 2014-07-11T02:25:54.817 回答
6

@Tammo Freese 给出的答案似乎是正确的(但没有提到所有主键)。我在文档中寻找一些证据。终于找到了:

Apple 文档提到了每类机密的主键(引用如下):

当该钥匙串已经具有具有同一组复合主键的同一类的项目时,系统认为该项目是给定钥匙串的副本。每个类的钥匙串项都有一组不同的主键,尽管所有类都使用了一些共同的属性。特别是,在适用的情况下, kSecAttrSynchronizable 和 kSecAttrAccessGroup 是主键集的一部分。下面列出了额外的每类主键:

  • 对于通用密码,主键包括 kSecAttrAccount 和 kSecAttrService。
  • 对于 Internet 密码,主键包括 kSecAttrAccount、kSecAttrSecurityDomain、kSecAttrServer、kSecAttrProtocol、kSecAttrAuthenticationType、kSecAttrPort 和 kSecAttrPath。
  • 对于证书,主键包括 kSecAttrCertificateType、kSecAttrIssuer 和 kSecAttrSerialNumber。
  • 对于关键项,主键包括kSecAttrKeyClass、kSecAttrKeyType、kSecAttrApplicationLabel、kSecAttrApplicationTag、kSecAttrKeySizeInBits和kSecAttrEffectiveKeySize。
  • 对于身份项,即证书和私钥捆绑在一起,主密钥与证书相同。因为私钥可能被多次认证,证书的唯一性决定了身份的唯一性。
于 2018-07-04T12:20:08.907 回答
-1

这是有关钥匙串项目唯一性的另一条有用信息,可在此 Apple 文档页面的“确保可搜索性”部分中找到。

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

如果您的应用确实添加了具有不同动态属性的项目,那么您需要在检索期间在它们之间进行选择。一种选择是以另一种方式记录有关项目的信息。例如,如果您在 Core Data 模型中保存用户记录,则在使用钥匙串服务存储密码字段后将用户名存储在那里。稍后,您使用从数据模型中提取的用户名来作为密码搜索的条件。

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

示例中使用了类项kSecClassInternetPassword,但有一条注释说:

钥匙串服务还提供相关的 kSecClassGenericPassword 项目类。通用密码在大多数方面与 Internet 密码相似,但它们缺少特定于远程访问的某些属性(例如,它们没有 kSecAttrServer 属性)。当您不需要这些额外属性时,请改用通用密码。

于 2020-03-18T15:33:58.380 回答