Keychain returns 每次调用中的不同值
Keychain returns different values in each call
我有一个 class,它将 NSDictionary
保存到 KeyChain 中。它工作正常,但突然间,当我尝试加载 NSDictionary
时,我得到了 nil 值。
这是class:
//
// KeyChainHandler.m
//
//
#import "KeyChainHandler.h"
#define IDENTIFIER @"Identifier"
@interface KeyChainHandler ()
@property (strong, nonatomic, readwrite) NSDictionary *applicationData;
@end
@implementation KeyChainHandler
// Make this class a singleton
static KeyChainHandler *instance = nil;
+ (KeyChainHandler*)sharedKeyChain
{
@synchronized(self)
{
if (!instance) {
instance = [[self alloc] init];
}
}
return instance;
}
- (id)init
{
self = [super init];
if (self)
{
[self load];
}
return self;
}
- (void)saveObject:(NSDictionary*)data
{
self.applicationData = data;
[self storeDictionary:data toKeychainWithKey:IDENTIFIER];
}
- (NSDictionary*)load
{
NSDictionary *data = [KeyChainHandler dictionaryFromKeychainWithKey:IDENTIFIER];
self.applicationData = data;
return data;
}
- (void)remove
{
[self deleteDictionaryFromKeychainWithKey:IDENTIFIER];
}
- (void)storeDictionary:(NSDictionary*)data toKeychainWithKey:(NSString*)aKey
{
// serialize dict
NSData *serializedDictionary = [NSKeyedArchiver archivedDataWithRootObject:data];
// encrypt in keychain
// first, delete potential existing entries with this key (it won't auto update)
[self remove];
// setup keychain storage properties
NSDictionary *storageQuery = @{
(__bridge id)kSecAttrAccount: aKey,
(__bridge id)kSecValueData: serializedDictionary,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlocked
};
OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef)storageQuery, nil);
if(osStatus != noErr) {
// do someting with error
}
}
+ (NSDictionary*)dictionaryFromKeychainWithKey:(NSString *)aKey
{
// setup keychain query properties
NSDictionary *readQuery = @{
(__bridge id)kSecAttrAccount: aKey,
(__bridge id)kSecReturnData: (id)kCFBooleanTrue,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword
};
CFDataRef serializedDictionary = NULL;
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef)readQuery, (CFTypeRef *)&serializedDictionary);
if(osStatus == noErr) {
// deserialize dictionary
NSData *data = (__bridge NSData *)serializedDictionary;
NSDictionary *storedDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return storedDictionary;
}
else {
// do something with error
return nil;
}
}
- (void)deleteDictionaryFromKeychainWithKey:(NSString*)aKey
{
// setup keychain query properties
NSDictionary *deletableItemsQuery = @{
(__bridge id)kSecAttrAccount: aKey,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll,
(__bridge id)kSecReturnAttributes: (id)kCFBooleanTrue
};
CFArrayRef itemList = nil;
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef)deletableItemsQuery, (CFTypeRef *)&itemList);
// each item in the array is a dictionary
NSArray *itemListArray = (__bridge NSArray *)itemList;
for (NSDictionary *item in itemListArray) {
NSMutableDictionary *deleteQuery = [item mutableCopy];
[deleteQuery setValue:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
// do delete
osStatus = SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
if(osStatus != noErr) {
// do something with error
}
}
}
@end
在 AppDelegate
处,当我打印 [[KeyChainHandler sharedHandler] load];
时,我得到了正确的数据,然后在登录屏幕上我再次尝试打印,结果得到 nil
。然后,当我重新启动(仅使用 CMD + R)应用程序时,我没有得到 nil
,我再次得到正确的数据..
好像是什么问题?也许这是某种 Apple 的错误?
原因:调用 [[KeyChainHandler sharedHandler] load];
,属性 已在创建单例时加载,如果更改 属性 也会更新。
您确实需要在 remove
中将 属性 设置为 nil
。
而是使用:
NSDictionary *dict = [KeyChainHandler sharedKeyChain].applicationData;
注:代码为:sharedKeyChain
,示例调用为:sharedHandler
.
我有一个 class,它将 NSDictionary
保存到 KeyChain 中。它工作正常,但突然间,当我尝试加载 NSDictionary
时,我得到了 nil 值。
这是class:
//
// KeyChainHandler.m
//
//
#import "KeyChainHandler.h"
#define IDENTIFIER @"Identifier"
@interface KeyChainHandler ()
@property (strong, nonatomic, readwrite) NSDictionary *applicationData;
@end
@implementation KeyChainHandler
// Make this class a singleton
static KeyChainHandler *instance = nil;
+ (KeyChainHandler*)sharedKeyChain
{
@synchronized(self)
{
if (!instance) {
instance = [[self alloc] init];
}
}
return instance;
}
- (id)init
{
self = [super init];
if (self)
{
[self load];
}
return self;
}
- (void)saveObject:(NSDictionary*)data
{
self.applicationData = data;
[self storeDictionary:data toKeychainWithKey:IDENTIFIER];
}
- (NSDictionary*)load
{
NSDictionary *data = [KeyChainHandler dictionaryFromKeychainWithKey:IDENTIFIER];
self.applicationData = data;
return data;
}
- (void)remove
{
[self deleteDictionaryFromKeychainWithKey:IDENTIFIER];
}
- (void)storeDictionary:(NSDictionary*)data toKeychainWithKey:(NSString*)aKey
{
// serialize dict
NSData *serializedDictionary = [NSKeyedArchiver archivedDataWithRootObject:data];
// encrypt in keychain
// first, delete potential existing entries with this key (it won't auto update)
[self remove];
// setup keychain storage properties
NSDictionary *storageQuery = @{
(__bridge id)kSecAttrAccount: aKey,
(__bridge id)kSecValueData: serializedDictionary,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlocked
};
OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef)storageQuery, nil);
if(osStatus != noErr) {
// do someting with error
}
}
+ (NSDictionary*)dictionaryFromKeychainWithKey:(NSString *)aKey
{
// setup keychain query properties
NSDictionary *readQuery = @{
(__bridge id)kSecAttrAccount: aKey,
(__bridge id)kSecReturnData: (id)kCFBooleanTrue,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword
};
CFDataRef serializedDictionary = NULL;
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef)readQuery, (CFTypeRef *)&serializedDictionary);
if(osStatus == noErr) {
// deserialize dictionary
NSData *data = (__bridge NSData *)serializedDictionary;
NSDictionary *storedDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return storedDictionary;
}
else {
// do something with error
return nil;
}
}
- (void)deleteDictionaryFromKeychainWithKey:(NSString*)aKey
{
// setup keychain query properties
NSDictionary *deletableItemsQuery = @{
(__bridge id)kSecAttrAccount: aKey,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll,
(__bridge id)kSecReturnAttributes: (id)kCFBooleanTrue
};
CFArrayRef itemList = nil;
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef)deletableItemsQuery, (CFTypeRef *)&itemList);
// each item in the array is a dictionary
NSArray *itemListArray = (__bridge NSArray *)itemList;
for (NSDictionary *item in itemListArray) {
NSMutableDictionary *deleteQuery = [item mutableCopy];
[deleteQuery setValue:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
// do delete
osStatus = SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
if(osStatus != noErr) {
// do something with error
}
}
}
@end
在 AppDelegate
处,当我打印 [[KeyChainHandler sharedHandler] load];
时,我得到了正确的数据,然后在登录屏幕上我再次尝试打印,结果得到 nil
。然后,当我重新启动(仅使用 CMD + R)应用程序时,我没有得到 nil
,我再次得到正确的数据..
好像是什么问题?也许这是某种 Apple 的错误?
原因:调用 [[KeyChainHandler sharedHandler] load];
,属性 已在创建单例时加载,如果更改 属性 也会更新。
您确实需要在 remove
中将 属性 设置为 nil
。
而是使用:
NSDictionary *dict = [KeyChainHandler sharedKeyChain].applicationData;
注:代码为:sharedKeyChain
,示例调用为:sharedHandler
.