我的问题涉及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)
经过长时间阅读苹果文档,博客和论坛条目后,我发现kSecClassGenericPassword
类型的钥匙串项从属性kSecAttrAccessGroup
,kSecAttrAccount
和{中获得了它的唯一性{1}}。
如果请求1中的这三个属性与请求2中的相同,则无论其他任何属性如何,您都会收到相同的通用密码keychain项。如果此属性中的一个(或两个或全部)更改其值,则会得到不同的项目。
但kSecAttrService
仅适用于kSecAttrService
类型的项目,因此它不能是任何其他类型项目的“唯一键”的一部分,并且似乎没有文档这清楚地指出了哪些属性唯一地确定了钥匙串项目。
“GenericKeychain”类“KeychainItemWrapper”中的示例代码使用属性kSecClassGenericPassword
使项目唯一,但这是一个错误。此示例中的两个条目仅存储为两个不同的条目,因为它们的kSecAttrGeneric
不同(一个设置了访问组,另一个允许它自由)。如果您尝试使用Apple的kSecAttrAccessGroup
添加没有访问组的第二个密码,则会失败。
所以,请回答我的问题:
KeychainItemWrapper
,kSecAttrAccessGroup
和kSecAttrAccount
的组合是kSecClass为kSecAttrService
的钥匙串项的“唯一键”? kSecClassGenericPassword
不是kSecClass
,则哪个属性会使钥匙串项目唯一?答案 0 :(得分:154)
主键如下(源自Apple的开源文件,请参阅Schema.m4,KeySchema.m4和SecItem.cpp):
kSecClassGenericPassword
的钥匙串项,主键是组合
kSecAttrAccount
和kSecAttrService
。kSecClassInternetPassword
的钥匙串项,主键是kSecAttrAccount
,kSecAttrSecurityDomain
,kSecAttrServer
,kSecAttrProtocol
,{{1}的组合},kSecAttrAuthenticationType
和kSecAttrPort
。kSecAttrPath
的钥匙串项,主键是kSecClassCertificate
,kSecAttrCertificateType
和kSecAttrIssuer
的组合。kSecAttrSerialNumber
的钥匙串项,主键是kSecClassKey
,kSecAttrApplicationLabel
,kSecAttrApplicationTag
的组合,
kSecAttrKeyType
,kSecAttrKeySizeInBits
以及SecItem尚未公开的创建者,开始日期和结束日期。kSecAttrEffectiveKeySize
的钥匙串项目,我没有找到开源文件中主键字段的信息,但由于标识是私钥和证书的组合,我假设主要key是kSecClassIdentity
和kSecClassKey
的主键字段的组合。由于每个钥匙串项属于钥匙串访问组,感觉钥匙串访问组(字段kSecClassCertificate
)是所有这些主键的添加字段。
答案 1 :(得分:8)
我前几天(在iOS 7.1上)遇到了与此问题相关的错误。我使用SecItemCopyMatching
来阅读kSecClassGenericPassword
项目,即使errSecItemNotFound
,kSecAttrAccessGroup
和kSecAttrAccount
成为kSecAttrService
,它也会一直返回kSecAttrAccessible
( - 25300)所有这些都与钥匙串中的项目相匹配。
最终我发现kSecAttrAccessibleAlways
不匹配。钥匙串中的值持有pdmn = dk(kSecAttrAccessibleWhenUnlocked
),但我使用的是SecItemCopyMatching
。
当然OSStatus
首先不需要此值,但errSecParam
不是errSecBadReq
也不是errSecItemNotFound
,而只是SecItemUpdate
( - 25300)这使得找到它有点棘手。
对于kSecAttrAccessible
我遇到了同样的问题,但在此方法中,即使在query
参数中使用相同的{{1}}也无效。只有完全删除此属性才能修复它。
我希望这条评论能为你们中的一些人节省一些宝贵的调试时间。
答案 2 :(得分:2)
@Tammo Freese给出的答案似乎是正确的(但未提及所有主键)。我正在文档中寻找证据。终于找到了:
Apple Documentation mentioning primary keys for each class of secret (quote below):
当该钥匙串已经具有相同类别的项目且具有相同的复合主键集时,系统认为该项目是给定钥匙串的重复项。每个类别的钥匙串项都有不同的主键集,尽管在所有类别中共用一些属性。特别是,在适用情况下,kSecAttrSynchronizable和kSecAttrAccessGroup是主键集的一部分。以下列出了每个类别的其他主键:
- 对于通用密码,主键包括kSecAttrAccount和 kSecAttrService。
- 对于Internet密码,主键包括kSecAttrAccount, kSecAttrSecurityDomain,kSecAttrServer,kSecAttrProtocol, kSecAttrAuthenticationType,kSecAttrPort和kSecAttrPath。
- 对于证书,主键包括kSecAttrCertificateType, kSecAttrIssuer和kSecAttrSerialNumber。
- 对于关键项,主键包括kSecAttrKeyClass, kSecAttrKeyType,kSecAttrApplicationLabel,kSecAttrApplicationTag, kSecAttrKeySizeInBits和kSecAttrEffectiveKeySize。
- 对于身份项目,这些身份项目是证书和私钥捆绑在一起的 一起,主键与证书相同。 私钥可能会被多次认证, 证书确定身份的身份。
答案 3 :(得分:0)
这是有关钥匙串项目唯一性的另一条有用信息,可在this Apple docs page的“确保可搜索性”部分中找到。
在示例中,使用了为了以后能够找到该项目,您将使用其属性的知识。在此示例中,服务器和帐户是商品的区别特征。对于常量属性(此处为服务器),在查找期间使用相同的值。相反,帐户属性是动态的,因为它保留了用户在运行时提供的值。 只要您的应用程序从不添加具有不同属性的相似项目(例如同一服务器上不同帐户的密码),就可以忽略这些动态属性作为搜索参数,而将它们与该项目一起检索。结果,当您查找密码时,您还将获得相应的用户名。
如果您的应用确实添加了具有不同动态属性的项目,则需要一种在检索过程中进行选择的方法。一种选择是以另一种方式记录有关项目的信息。例如,如果将用户记录保留在Core Data模型中,则在使用钥匙串服务存储密码字段之后,将用户名存储在该数据库中。稍后,您将使用从数据模型中拉出的用户名来限制密码的搜索。
在其他情况下,可以通过添加更多属性来进一步表征商品。例如,您可以在原始添加查询中包含
kSecAttrLabel
属性,并提供一个标记该项目以达到特定目的的字符串。然后,您将可以使用此属性在以后缩小搜索范围。
类kSecClassInternetPassword
的项,但有一条注释指出:
钥匙串服务还提供了相关的kSecClassGenericPassword项目类。通用密码在大多数方面与Internet密码相似,但是通用密码缺少某些特定于远程访问的属性(例如,它们没有kSecAttrServer属性)。当您不需要这些额外的属性时,请改用通用密码。