无法在钥匙串中正确存储值
Posted
技术标签:
【中文标题】无法在钥匙串中正确存储值【英文标题】:Unable to store values in keychain correctly 【发布时间】:2013-11-14 13:12:51 【问题描述】:我正在使用以下内容在钥匙串中存储值:
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"suggest" accessGroup:nil];
[keychain setObject:[object valueForKey:@"token"] forKey:(__bridge id)(kSecValueData)];
[keychain setObject:[object valueForKey:@"usr_id"] forKey:(__bridge id)(kSecAttrAccount)];
这是我检索值的代码:
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"suggest" accessGroup:nil];
NSLog(@"TOKEN:%@",[keychain objectForKey:(__bridge id)(kSecValueData)]);
NSLog(@"USER NAME:%@",[keychain objectForKey:(__bridge id)(kSecAttrAccount)]);
我还包含了安全框架。
我已经搜索了 *** 和 google,但无法在 NSLog
中获取值。
可能是什么原因?
编辑:
如果有人有其他信息,也可以向我提供其他信息,以便使用钥匙串?
【问题讨论】:
您的代码应该可以工作 - 您确定您的字典object
在您写入钥匙串时确实保存了数据。
是的,我在应用程序级别使用相同的变量,但我需要继续使用钥匙串。它确实具有价值。应该是其他问题。
您是否尝试过您的代码在设置它们后立即检索值?
@AncAinu:是的,我有。我想要的只是将身份验证令牌和 user_id 存储在钥匙串中,并随时获取它们。我搜索了很多,但找不到任何方法。
很奇怪,你真的确定[object valueForKey:@"token"]
给点东西,你有没有在检查后做了一个NSLog?因为 KeychainItemWrapper
中的 setObject:forKey
方法不会断言是否给出了 nil 值,它只是执行 return;
所以在这种情况下不会出错。
【参考方案1】:
我使用答案是因为评论有限... 我从一个新项目中尝试了以下代码,下载了 KeychainItemWrapper 并链接了 Security.framework
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"suggest" accessGroup:nil];
[keychain setObject:@"aaaa" forKey:(__bridge id)(kSecValueData)];
[keychain setObject:@"bbbb" forKey:(__bridge id)(kSecAttrAccount)];
NSLog(@"TOKEN:%@",[keychain objectForKey:(__bridge id)(kSecValueData)]);
NSLog(@"USER NAME:%@",[keychain objectForKey:(__bridge id)(kSecAttrAccount)]);
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"suggest" accessGroup:nil];
NSLog(@"TOKEN:%@",[keychain objectForKey:(__bridge id)(kSecValueData)]);
NSLog(@"USER NAME:%@",[keychain objectForKey:(__bridge id)(kSecAttrAccount)]);
// Override point for customization after application launch.
return YES;
一切都很完美。我想您需要提供更多代码才能找到答案。这是我的日志:
2013-11-19 17:11:08.378 test[3430:a0b] TOKEN:aaaa
2013-11-19 17:11:08.379 test[3430:a0b] USER NAME:bbbb
2013-11-19 17:11:08.380 test[3430:a0b] TOKEN:aaaa
2013-11-19 17:11:08.381 test[3430:a0b] USER NAME:bbbb
【讨论】:
您所说的“下载...并链接 Security.framework”是什么意思?我也从项目设置中链接了它。还有什么事情要做??? 哦,对不起,这是“下载 KeychainItemWrapper 并链接 Security.framework”。这是我的 dl 的链接:dropbox.com/s/mjkm8u0z2ybhcvf/KeyChainWrapper_ARCSupported.zip 谢谢你的努力兄弟。 :) 你找到问题了吗? 您的新“Keychainitemwrapper”文件有效。可能是我使用的那个会有任何问题。谢谢你帮助我。最后一个问题,如果我想在钥匙串中存储两个以上的值怎么办?我该怎么做?【参考方案2】:接下来是工作代码,可让您维护与主“键”(此处为电子邮件地址,可以是应用中的任何主标识符)相关联的数据字典。
其他键存储在任意大小的字典中,并放置在“密码”keyChain 对象中。
首先,我把它放在我的 AppDelegate.h 文件的底部:
#define kEmail @"email"
#define kPassword @"pass"
#define kSomeOtherItemA @"a_item"
#define kSomeOtherItemB @"b_item"
@interface AppDelegate (Keychain)
- (NSDictionary *)keychainDictionary; // current keyChain dictionary
- (void)eraseKeychain; // total wipe it
- (void)erasePassword; // wipe the password
@end
您可以在此处添加您认为合适的特殊方法。通过放置这是 AppDelegate.h 文件,导入该文件的任何其他类都可以使用这些方法。如果您希望它私有,请将其移至您的 AppDelegate.m 文件
这在您的 AppDelegate.m 文件中,在顶部:
@interface AppDelegate (KeychainPrivate)
- (void)updateKeychainItem:(NSString *)item forKey:(NSString *)key; //the password, or itemA, or itemB
- (id)keychainItemForKey:(NSString *)key; // the password, itemA, itemB, ...
@end
这可以放在 AppDelegate.m 的底部或单独的文件中:
static char *kcIdentifier = "foo"; // use any string you want instead of "foo"
@implementation AppDelegate (Keychain)
- (KeychainItemWrapper *)keyChainItemWrapper
static dispatch_once_t pred;
static KeychainItemWrapper *kciw;
dispatch_once(&pred, ^
kciw = [[KeychainItemWrapper alloc] initWithIdentifier:kcIdentifier accessGroup:nil];
);
return kciw;
- (NSDictionary *)keychainDictionary
NSMutableDictionary *mDict = [NSMutableDictionary dictionaryWithCapacity:3];
KeychainItemWrapper *kcItem = [self keyChainItemWrapper];
NSString *emailAddr = [kcItem objectForKey:(__bridge id)kSecAttrAccount];
if([emailAddr length])
NSData *data = [kcItem objectForKey:(__bridge id)kSecValueData];
if([data length])
NSKeyedUnarchiver *kua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
NSDictionary *dict = [kua decodeObject];
[kua finishDecoding];
[mDict addEntriesFromDictionary:dict];
mDict[kEmail] = emailAddr; // last in case it got in the dictionary somehow
return mDict;
- (void)eraseKeychain
KeychainItemWrapper *kcItem = [self keyChainItemWrapper];
[kcItem resetKeychainItem];
- (void)erasePassword
[self updateKeychainItem:@"" forKey:kPassword];
@end
@implementation AppDelegate (KeychainPrivate)
- (void)updateKeychainItem:(NSString *)item forKey:(NSString *)key
KeychainItemWrapper *kcItem = [self keyChainItemWrapper];
// NSLog(@"SET KC key=%@ ITEM %@", key, item);
// NSLog(@"CURRENT KEYCHAIN: %@", [self keychainDictionary]);
if([key isEqualToString:kEmail])
NSString *emailAddr = [kcItem objectForKey:(__bridge id)kSecAttrAccount];
if(![emailAddr isEqualToString:item])
[kcItem setObject:item forKey:(__bridge id)kSecAttrAccount];
// NSLog(@"SET KC Account ITEM %@", item);
// NSLog(@"KC Account ITEM %@ ALREADY SET", item);
else
NSData *data = [kcItem objectForKey:(__bridge id)kSecValueData];
//NSLog(@"KC get DATA len=%d", [data length]);
NSDictionary *dict;
if([data length])
NSKeyedUnarchiver *kua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
dict = [kua decodeObject];
[kua finishDecoding];
else
dict = [NSDictionary dictionary];
//NSLog(@"KC OLD DICT %@", dict);
if(![item isEqualToString:dict[key]])
//NSLog(@"KC DATA NOT EQUAL");
NSMutableDictionary *mDict = [NSMutableDictionary dictionaryWithDictionary:dict];
mDict[key] = item;
NSMutableData *tmpData = [NSMutableData dataWithCapacity:256];
NSKeyedArchiver *ka = [[NSKeyedArchiver alloc] initForWritingWithMutableData:tmpData];
[ka encodeObject:mDict];
[ka finishEncoding];
//NSLog(@"KC ENCODE MDICT %@", mDict);
[kcItem setObject:tmpData forKey:(__bridge id)kSecValueData];
//NSLog(@"SET KC DATA LEN=%d", [tmpData length]);
//NSLog(@"JUST UPDATED KEYCHAIN KEY=%@ val=%@ read dict back=%@", key, item, [self keychainDictionary]);
- (id)keychainItemForKey:(NSString *)key
NSDictionary *dict = [self keychainDictionary];
return dict[key];
@end
您还需要 KeyChainItemWrapper 类。你可以得到一个 ARCified 并稍作修改(以便处理字典)KeyChainItemWrapper version。您真的可以使用任何您想要的 KeyChainWrapper 文件,但需要进行此更改(这是除 ARCifying 之外对 Apple 代码的唯一更改):
// Default data for keychain item.
#ifndef PASSWORD_USES_DATA
[keychainItemData setObject:@"" forKey:(__bridge id)kSecValueData];
#else
[keychainItemData setObject:[NSData data] forKey:(__bridge id)kSecValueData];
#endif
如您所见,如果您愿意,可以取消注释很多日志消息以查看它的运行情况。
此代码用于运输应用程序。
【讨论】:
【参考方案3】:如果您使用xCode 8.0
,请执行以下操作:
Project Navigator -> Select Target -> Capabilities
启用KeyChain Sharing
ON
【讨论】:
以上是关于无法在钥匙串中正确存储值的主要内容,如果未能解决你的问题,请参考以下文章