secItemCopyMatching返回零数据

首先,我观看了WWDC 2013年关于保护钥匙链秘密的会议。 我想要做一个基本的密码存储。 观看了整个video,但在video的前10分钟find了我需要的内容。 这似乎很直接,但我不完全了解数据编码和检索工作。

问题:在secItemCopyMatching后,我检查我的NSData对象,以确保它不是零,然后将其转换为NSString。 问题是,它总是零。 下面是我如何保存钥匙链条目或更新,接下来是我如何检索它。 任何帮助和解释将非常感激。

更新(编辑):果味极客,谢谢你的回应。 我使用__bridge更新了我的代码。 我的问题现在归结为,我正确地存储和检索密码? 我有两个错误或只是一个或另一个? 我的NSData实例始终为零。 我正在检查返回代码,我的SecItemAdd和SecItemUpdate(当keychaing条目存在时)正常工作。 我似乎无法检索存储的数据(密码)的string值,以将其与用户input的密码进行比较。 感谢帮助家伙和gals。 这是我现在正在做的事情:

更新#2 :(用Fruity Geek的答案和最终工作版本进行编辑,我的编辑只包括对以下代码的更改。

设置钥匙串条目:

NSData *secret = [_backupPassword dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *query = @{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrService: twServiceName, (__bridge id)kSecAttrAccount: twAccountName, (__bridge id)kSecValueData: secret, }; OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, NULL); if (status == errSecDuplicateItem) { // this item exists in the keychain already, update it query = @{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrService: twServiceName, (__bridge id)kSecAttrAccount: twAccountName, }; NSDictionary *changes = @{ (__bridge id)kSecValueData: secret, }; status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)changes); } 

从钥匙串中取回密码:

 NSDictionary *query = @{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrService: twServiceName, (__bridge id)kSecAttrAccount: twAccountName, (__bridge id)kSecReturnData: @YES, }; NSData *data = NULL; CFTypeRef dataTypeRef = (__bridge CFTypeRef)data; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataTypeRef); NSData *data = (__bridge NSData *)dataTypeRef; NSString *passcode = @"none"; if (status == errSecSuccess) { // we found a keychain entry, set the passcode if (data) passcode = [NSString stringWithUTF8String:[data bytes]]; } 

twServiceName和twAccountName是静态NSStrings。

正如我所说的,我不太清楚我正在用__bridge或CFTypeRef做什么。 我查看了苹果文档,在这里和其他网站的许多post,但钥匙链和这些条款是全新的我,我仍然试图弄清楚。 希望这里有人能指出我的错误,并帮助我理解。 先谢谢您的帮助。

iOS 7 / Xcode 5

您不拥有任何Core Foundation对象(您没有创build或复制它们),也不想保留或释放它们,因此CFBridgingReleaseCFBridgingRetain不正确。 使用(__bridge id)代替只要你想转换到一个Objective-C对象。

 (__bridge id)kSecAttrService 

什么时候应该使用__bridge与CFBridgingRelease / CFBridgingRetain?

你的数据variables和dataTypeRef是两个不同的指针。 只有dataTypeRef在SecItemCopyMatching中填充了数据。 在你的CFTypeRef被SecItemCopyMatching填充之后将其转换为NSData 这样你的数据并不总是为零

 CFTypeRef dataTypeRef = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataTypeRef); NSData *data = (__bridge NSData *)dataTypeRef; 

你应该仔细看看所有的SecItem函数调用返回的OSStatus。 有很多可能的返回代码不成功。 在你的情况下,你正在SecItemAdd中检测到重复的项目 – 然后更新到完全相同的项目(什么也不做)。 相反,你应该尝试使用SecItemCopyMatching首先检索它。 如果找不到匹配,请使用SecItemAdd。 如果find匹配项,请使用SecItemUpdate。

苹果公司的示例代码非常糟糕 ,不是为ARC编写的,而是令人困惑,但它存在。 特别是, writeToKeychain方法是你所需要的。 https://developer.apple.com/library/ios/documentation/Security/Conceptual/keychainServConcepts/iPhoneTasks/iPhoneTasks.html#//apple_ref/doc/uid/TP30000897-CH208-SW1