Tips:在实习过程中,有个需求是实现App的七天免登陆。当时的想法是用NSUserDefaults存储,用NSDate判断时间,取七天的间隔判断登陆状态。现在想想用NSUserDefaults直接存储用户信息是非常不安全的。通常情况下,可以用NSUserDefaults存储数据信息,但是对于一些私密信息,比如账号、密码等等,就需要使用更为安全的KeyChain了。而KeyChain的信息是存在于每个应用(app)的沙盒之外的,所以KeyChain里保存的信息不会因App被删除而丢失,在用户重新安装App后依然有效,数据还在。


2016.12.22最新更新
最近一直有朋友说不知道怎么用,特来更新了 PDKeyChain,添加了注释,有问题可以提 issue 欢迎讨论。


2016.8.23最新更新
最近一直有朋友问源码,整理了一下发在这里


2016.3.23最新更新
近期项目中又用到keychain,于是回头翻了翻以前写的代码,感觉写的/(ㄒoㄒ)/~~
设计不科学,命名不规范,怎么看怎么别扭,那就重写吧~

KeyChain

话不多说,咱们直接来看怎么快速集成KeyChain。

  1. 自定义一个类,取名XXXKeyChain,如下:
1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>
#import <Security/Security.h>
#define KEY_PASSWORD @"com.rry.app.password"
#define KEY_USERNAME_PASSWORD @"com.rry.app.usernamepassword"
@interface RRYKeyChain : NSObject
+ (void)save:(NSString *)service data:(id)data;
+ (id)load:(NSString *)service;
+ (void)delete:(NSString *)service;
@end

以上代码自定义了三个方法,存、取、删。并且定义了几个字符串用来做key。当然,想使用keychain请不要忘记引入Security包,引入文件 #import<Security/Security.h>

  1. 再来看.m文件的具体实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#import "RRYKeyChain.h"

@implementation RRYKeyChain
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword,(id)kSecClass,
service, (id)kSecAttrService,
service, (id)kSecAttrAccount,
(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
nil];
}

+ (void)save:(NSString *)service data:(id)data {
//Get search dictionary
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Delete old item before add new item
SecItemDelete((CFDictionaryRef)keychainQuery);
//Add new object to search dictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
//Add item to keychain with the search dictionary
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
}

+ (id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Configure the search setting
//Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
[keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
@try {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)keyData];
} @catch (NSException *e) {
NSLog(@"Unarchive of %@ failed: %@", service, e);
} @finally {
}
}
if (keyData)
CFRelease(keyData);
return ret;
}

+ (void)delete:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)keychainQuery);
}

然后就是存,其中password是要存的密码字符串。

1
2
3
NSMutableDictionary *usernamepasswordKVPairs = [NSMutableDictionary dictionary];
[usernamepasswordKVPairs setObject:password forKey:KEY_PASSWORD];
[RRYKeyChain save:KEY_USERNAME_PASSWORD data:usernamepasswordKVPairs];

1
2
3
NSMutableDictionary *usernamepasswordKVPairs = (NSMutableDictionary *)[RRYKeyChain load:KEY_USERNAME_PASSWORD];

NSLog(@"%@",[usernamepasswordKVPairs objectForKey:KEY_PASSWORD]);

1
[RRYKeyChain delete:KEY_USERNAME_PASSWORD];

搞定收工,使用KeyChain快速存储密码。


2016.3.23最新更新

话不多说,直接上代码,首先是.h

1
2
3
4
5
6
7
8
9
10
11
12
#import <Foundation/Foundation.h>
#import <Security/Security.h>

@interface RHKeyChain : NSObject

+ (void)rhKeyChainSave:(NSString *)service;

+ (NSString *)rhKeyChainLoad;

+ (void)rhKeyChainDelete:(NSString *)service;

@end

只对外提供三个方法,存、取、删,实现放在内部,再是.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static NSString * const kRHDictionaryKey = @"com.xxxx.dictionaryKey";
static NSString * const kRHKeyChainKey = @"com.xxxx.keychainKey";

+ (void)rhKeyChainSave:(NSString *)service {
    NSMutableDictionary *tempDic = [NSMutableDictionary dictionary];
    [tempDic setObject:service forKey:kRHDictionaryKey];
    [self save:kRHKeyChainKey data:tempDic];
}

+ (NSString *)rhKeyChainLoad{
    NSMutableDictionary *tempDic = (NSMutableDictionary *)[self load:kRHKeyChainKey];
    return [tempDic objectForKey:kRHDictionaryKey];
}

+ (void)rhKeyChainDelete{
    [self delete:kRHKeyChainKey];
}

三个方法的实现。