iOS的keyChain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式,每个ios程序都有一个独立的keychain存储。相对于NSUserDefaults、文件保存等一般方式,keychain保存更为安全,而且keychain里保存的信息不会因App被删除而丢失。
使用keychain(钥匙串)存储可以保证程序卸载重装以及iOS系统升级UUID都不变。但是如果把整个系统还原以后,UUID还是会改变的。但这仍是目前为止最佳的解决办法了。
比如:百度的两个APP,一个APP登陆了,打开另一个APP就会自动登陆,用的就是keyChain
1.App间共享
需要共享的App,都要打开Keychain Sharing,添加App的Bundle Identifier,“com.test.one”和“com.test.two”是两个App的Bundle Identifier
KeyChainTool.h
#import <Foundation/Foundation.h>
@interface KeyChainTool : NSObject
/*!
保存数据
@data 要存储的数据
@identifier 存储数据的标示
*/
+(BOOL)saveData:(id)data withIdentifier:(NSString*)identifier;
/*!
读取数据
*/
+(id)readData:(NSString*)identifier;
/*!
更新数据
@data 要更新的数据
*/
+(BOOL)updata:(id)data withIdentifier:(NSString*)identifier;
/*!
删除数据
*/
+(void)Delete:(NSString*)identifier;
@end
KeyChainTool.m
#import "KeyChainTool.h"
#import <Security/Security.h>
@implementation KeyChainTool
/*!
//kSecClass 密钥类型键
1.kSecClassGenericPassword 普通的密码类型
2.kSecClassInternetPassword 互联网密码类型
3.kSecClassCertificate 证书类型
4.kSecClassKey 加密的键类型
5.kSecClassIdentity 身份类型
//kSecAttrAccessible 可访问性类型
1.kSecAttrAccessibleWhenUnlocked //keychain项受到保护,只有在设备未被锁定时才可以访问 ,备份
2.kSecAttrAccessibleAfterFirstUnlock //keychain项受到保护,直到设备启动并且用户第一次输入密码,备份
3.kSecAttrAccessibleAlways //keychain未受保护,任何时候都可以访问 (Default),备份
4.kSecAttrAccessibleWhenUnlockedThisDeviceOnly //keychain项受到保护,只有在设备未被锁定时才可以访问,而且不可以转移到其他设备,不备份
5.kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly //keychain项受到保护,直到设备启动并且用户第一次输入密码,而且不可以转移到其他设备,不备份
6.kSecAttrAccessibleAlwaysThisDeviceOnly //keychain未受保护,任何时候都可以访问,但是不能转移到其他设备,不备份
*/
+(NSMutableDictionary*) keyChainIdentifier:(NSString*)identifier {
return [@{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService : identifier,//[NSBundle mainBundle].bundleIdentifier,//服务
(__bridge id)kSecAttrAccount : identifier,//账户名
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAfterFirstUnlock
} mutableCopy];
}
/*!
保存数据
*/
+(BOOL)saveData:(id)data withIdentifier:(NSString*)identifier {
// 获取存储的数据的条件
NSMutableDictionary * saveQueryMutableDictionary = [self keyChainIdentifier:identifier];
// 删除旧的数据
SecItemDelete((CFDictionaryRef)saveQueryMutableDictionary);
// 设置新的数据
[saveQueryMutableDictionary setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
// 添加数据
OSStatus saveState = SecItemAdd((CFDictionaryRef)saveQueryMutableDictionary, nil);
// 释放对象
saveQueryMutableDictionary = nil ;
// 判断是否存储成功
if (saveState == errSecSuccess) {
return YES;
}
return NO;
}
/*!
读取数据
*/
+(id)readData:(NSString*)identifier {
id idObject = nil ;
// 通过标记获取数据查询条件
NSMutableDictionary * keyChainReadQueryMutableDictionary = [self keyChainIdentifier:identifier];
// 这是获取数据的时,必须提供的两个属性
// TODO: 查询结果返回到 kSecValueData
[keyChainReadQueryMutableDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
// TODO: 只返回搜索到的第一条数据
[keyChainReadQueryMutableDictionary setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
// 创建一个数据对象
CFDataRef keyChainData = nil ;
// 通过条件查询数据
if (SecItemCopyMatching((CFDictionaryRef)keyChainReadQueryMutableDictionary , (CFTypeRef *)&keyChainData) == noErr){
@try {
idObject = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)(keyChainData)];
} @catch (NSException * exception){
NSLog(@"Unarchive of search data where %@ failed of %@ ",identifier,exception);
}
}
if (keyChainData) {
CFRelease(keyChainData);
}
// 释放对象
keyChainReadQueryMutableDictionary = nil;
// 返回数据
return idObject ;
}
/*!
更新数据
@data 要更新的数据
@identifier 数据存储时的标示
*/
+(BOOL)updata:(id)data withIdentifier:(NSString*)identifier {
// 通过标记获取数据更新的条件
NSMutableDictionary * keyChainUpdataQueryMutableDictionary = [self keyChainIdentifier:identifier];
// 创建更新数据字典
NSMutableDictionary * updataMutableDictionary = [NSMutableDictionary dictionaryWithCapacity:0];
// 存储数据
[updataMutableDictionary setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
// 获取存储的状态
OSStatus updataStatus = SecItemUpdate((CFDictionaryRef)keyChainUpdataQueryMutableDictionary, (CFDictionaryRef)updataMutableDictionary);
// 释放对象
keyChainUpdataQueryMutableDictionary = nil;
updataMutableDictionary = nil;
// 判断是否更新成功
if (updataStatus == errSecSuccess) {
return YES ;
}
return NO;
}
/*!
删除数据
*/
+(void)Delete:(NSString*)identifier {
// 获取删除数据的查询条件
NSMutableDictionary * keyChainDeleteQueryMutableDictionary = [self keyChainIdentifier:identifier];
// 删除指定条件的数据
SecItemDelete((CFDictionaryRef)keyChainDeleteQueryMutableDictionary);
// 释放内存
keyChainDeleteQueryMutableDictionary = nil ;
}
@end
使用:
[KeyChainTool saveData:@"jin" withIdentifier:@"Keychain"];
NSString *Str = [KeyChainTool readData:@"Keychain"];
NSLog(@"%@,%@",Str);
2.保存UUID
AppDelegate.m
#import "AppDelegate.h"
#import "KeyChainTool.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSString *UUID = [[UIDevice currentDevice].identifierForVendor UUIDString];
[KeyChainTool saveData:UUID withIdentifier:@"UUID"];
return YES;
}
@end