iOS使用keychain存储数据
在某个周六的时候,我跑去听了一个淘宝举办的无线交流会,对于安全来说还是蛮有收获的。回来后在网上找相关资料,看到一个关于使用 keychain 来存储数据的方案,思路比较不错。
使用keychain,有2点好处。
1、数据相对使用NSUserDefaults,更安全一点。
2、可以用来进行应用间的数据共享。
下面给出通用类代码,首先是头文件
KeychainManager.h
#import <Foundation/Foundation.h>
@interface KeychainManager : NSObject
+ (void)save:(NSString *)key withValue:(id)value;
+ (id)get:(NSString *)key;
+ (void)deleteData;
@end
接下来,是具体的实现代码
KeychainManager.m
#import “KeychainManager.h”
@implementation KeychainManager
static NSString * const KEY_IN_KEYCHAIN = @”com.5danyuan.app”;
+ (void)save:(NSString *)key withValue:(id)value{
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setObject:value forKey:key];
NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge_transfer id)kSecClassGenericPassword,
(__bridge_transfer id)kSecClass,
KEY_IN_KEYCHAIN,(__bridge_transfer id)kSecAttrService,
KEY_IN_KEYCHAIN,(__bridge_transfer id)kSecAttrAccount,
(__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,
(__bridge_transfer id)kSecAttrAccessible,
nil];
//删除旧数据
SecItemDelete((__bridge_retained CFDictionaryRef)query);
[query setObject:[NSKeyedArchiver archivedDataWithRootObject:dic] forKey: (__bridge_transfer id)kSecValueData];
SecItemAdd((__bridge_retained CFDictionaryRef)query, NULL);
}
+ (id)get:(NSString *)key{
NSMutableDictionary *dic = nil;
NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge_transfer id)kSecClassGenericPassword,
(__bridge_transfer id)kSecClass,
KEY_IN_KEYCHAIN,(__bridge_transfer id)kSecAttrService,
KEY_IN_KEYCHAIN,(__bridge_transfer id)kSecAttrAccount,
(__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,
(__bridge_transfer id)kSecAttrAccessible,
nil];
[query setObject: (id)kCFBooleanTrue forKey: (__bridge_transfer id)kSecReturnData];
[query setObject: (__bridge_transfer id)kSecMatchLimitOne forKey: (__bridge_transfer id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((__bridge_retained CFDictionaryRef)query, (CFTypeRef *)&keyData) == noErr) {
@try {
dic = (NSMutableDictionary *)[NSKeyedUnarchiver unarchiveObjectWithData: (__bridge_transfer NSData *)keyData];
}
@catch (NSException *exception) {
NSLog(@”Unarchive of %@ failed:%@”,KEY_IN_KEYCHAIN,exception);
}
@finally {
}
}
if(dic != nil){
return [dic objectForKey:key];
}
return nil;
}
+ (void)deleteData{
NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge_transfer id)kSecClassGenericPassword,
(__bridge_transfer id)kSecClass,
KEY_IN_KEYCHAIN,(__bridge_transfer id)kSecAttrService,
KEY_IN_KEYCHAIN,(__bridge_transfer id)kSecAttrAccount,
(__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,
(__bridge_transfer id)kSecAttrAccessible,
nil];
SecItemDelete((__bridge_retained CFDictionaryRef)query);
}
@end
对于通用类而言,提供了3个方法,添加、删除、保存。其中,为了安全起见,防止被恶意分析,获取的get方法的返回值是 id 类型的。对于合法的使用者,自然是知道应该转换为什么样的数据类型的。