KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@”Password” accessGroup:nil];
标识符(Identifier)在后面我们要从keychain中取数据的时候会用到。如果你想要在应用之间共享信息,那么你需要指定访问组(access group)。有同样的访问组 的应用能够访问同样的keychain信息。
代码如下:
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@”Account Number” accessGroup:@”YOUR_APP_ID_HERE.com.yourcompany.GenericKeychainSuite”];
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)要把信息保存到keychain中,使用 setObject:forKey: 方法。在这里, (id)kSecAttrAccount 是一个预先定义好的键(key),我们可以用它来保存账号名称。 kSecClass指定了我们要保存的某类信息,在这里是一个通用的密码。kSecValueData可以被用来保存任意的数据,在这里是一个密码。
代码如下:
[wrapper setObject:kSecClassGenericPassword forKey:(id)kSecClass];
[wrapper setObject:@"username" forKey:(id)kSecAttrAccount];
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)[wrapper setObject:@"password"forKey:(id)kSecValueData];
[wrapper setObject:(id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(id)kSecAttrAccessible];
kSecAttrAccessiblein变量用来指定这个应用合适需要访问这个数据。我们需要对这个选项特别注意,并且使用最严格的选项。这个键(key)可以设置6种值。
当然,我们应该绝对不要使用kSecAttrAccessibleAlways。一个安全点的选项是kSecAttrAccessibleWhenUnlocked。有些选项是以 ThisDeviceOnly 结尾的,如果选中了这个选项,那么数据就会被以硬件相关的密钥(key)加密,因此不能被传输到或者被其他设备看到。即使它们提供了进一步的安全性,使用它们可能不是一个好主意,除非你有一个更好的理由不允许数据在备份之间迁移。
要从keychain中获取数据,可以用 NSString *accountName = [wrapper objectForKey:(id)kSecAttrAccount];
钥匙串中的条目称为SecItem,但它是存储在CFDictionary中的。SecItemRef类型并不存在。SecItem有五类:通用密码、互联网密码、证书、密钥和身份。在大多数情况下,我们用到的都是通用密码。许多问题都是开发人员尝试用互联网密码造成的。互联网密码要复杂得多,而且相比之下优势寥寥无几,除非开发Web浏览器,否则没必要用它。KeyChainItemWrapper只使用通用密码,这也是我喜欢它的原因之一。iOS应用很少将密钥和身份存储起来,所以我们在本书中不会讨论这方面的内容。只有公钥的证书通常应该存储在文件中,而不是钥匙串中。
最后,我们需要在钥匙串中搜索需要的内容。密钥有很多个部分可用来搜索,但最好的办法是将自己的标识符赋给它,然后搜索。通用密码条目都包含属性kSecAttrGeneric,可以用它来存储标识符。这也是KeyChainItemWrapper的处理方式。
钥匙串中的条目都有几个可搜索的**属性**和一个加密过的**值**。对于通用密码条目,比较重要的属性有账户(kSecAttrAccount)、服务(kSecAttrService)和标识符(kSecAttrGeneric)。而值通常是密码。
说明:
每一个keyChain的组成如图,整体是一个字典结构.
1.kSecClass key 定义属于那一种类型的keyChain
2.不同的类型包含不同的Attributes,这些attributes定义了这个item的具体信息
3.每个item可以包含一个密码项来存储对应的密码
二、Keychain操作
iOS中Security.framework框架提供了四个主要的方法来操作KeyChain:
代码如下:
// 查询
OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result);
// 添加
OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result);
// 更新
KeyChain中的ItemOSStatus SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
// 删除
KeyChain中的ItemOSStatus SecItemDelete(CFDictionaryRef query)
三、Keychain使用
引入Security包,引入文件 #import Security/Security.h
添加
代码如下:
- (IBAction)add:()sender {
(nameField.text.length && passwordField.text.length ) {
NSMutableDictionary* dic = [NSMutableDictionary dictionary];
[dic setObject:()kSecClassGenericPassword forKey:()kSecClass];
[dic setObject:nameField.text forKey:()kSecAttrAccount];
[dic setObject:[passwordField.text dataUsingEncoding:NSUTF8StringEncoding] forKey:()kSecValueData];
OSStatus s = SecItemAdd((CFDictionaryRef)dic, NULL);
NSLog(,s);
}
}
查找
代码如下:
- (IBAction)sel:()sender {
NSDictionary* query = [NSDictionary dictionaryWithObjectsAndKeys:kSecClassGenericPassword,kSecClass,
kSecMatchLimitAll,kSecMatchLimit,
kCFBooleanTrue,kSecReturnAttributes,nil];
CFTypeRef result = nil;
OSStatus s = SecItemCopyMatching((CFDictionaryRef)query, &result);
NSLog(,s);
NSLog(,result);
}
- (IBAction)sname:()sender {
(nameField.text.length ) {
NSDictionary* query = [NSDictionary dictionaryWithObjectsAndKeys:kSecClassGenericPassword,kSecClass,
nameField.text,kSecAttrAccount,
kCFBooleanTrue,kSecReturnAttributes,nil];
CFTypeRef result = nil;
OSStatus s = SecItemCopyMatching((CFDictionaryRef)query, &result);
NSLog(,s); NSLog(,result);
(s == noErr) {
NSMutableDictionary* dic = [NSMutableDictionary dictionaryWithDictionary:result];
[dic setObject:()kCFBooleanTrue forKey:kSecReturnData];
[dic setObject:[query objectForKey:kSecClass] forKey:kSecClass];
NSData* data = nil;
(SecItemCopyMatching((CFDictionaryRef)dic, (CFTypeRef*)&data) == noErr) {
(data.length)
NSLog(,[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);
}
}
}
}
修改
代码如下:
- (IBAction)update:()sender {
(nameField.text.length && passwordField.text.length ) {
NSDictionary* query = [NSDictionary dictionaryWithObjectsAndKeys:kSecClassGenericPassword,kSecClass,
nameField.text,kSecAttrAccount,
kCFBooleanTrue,kSecReturnAttributes,nil];
CFTypeRef result = nil;
(SecItemCopyMatching((CFDictionaryRef)query, &result) == noErr)
{
NSMutableDictionary* update = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary*)result];
[update setObject:[query objectForKey:kSecClass] forKey:kSecClass];
[update setObject:[passwordField.text dataUsingEncoding:NSUTF8StringEncoding] forKey:kSecValueData];
[update removeObjectForKey:kSecClass];
TARGET_IPHONE_SIMULATOR
[update removeObjectForKey:()kSecAttrAccessGroup];
NSMutableDictionary* updateItem = [NSMutableDictionary dictionaryWithDictionary:result];
[updateItem setObject:[query objectForKey:()kSecClass] forKey:()kSecClass];
OSStatus status = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)update);
NSLog(,status);
删除
代码如下:
- (IBAction)del:()sender {
(nameField.text.length ) {
NSDictionary* query = [NSDictionary dictionaryWithObjectsAndKeys:kSecClassGenericPassword,kSecClass,
nameField.text,kSecAttrAccount,nil];
OSStatus status = SecItemDelete((CFDictionaryRef)query);
NSLog(,status); }
}
四、保存密码实例
来看一下使用keychain保存密码的例子:
代码如下:
@implementation WQKeyChain
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge_transfer id)kSecClassGenericPassword,(__bridge_transfer id)kSecClass,
service, (__bridge_transfer id)kSecAttrService,
service, (__bridge_transfer id)kSecAttrAccount,
(__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,(__bridge_transfer 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((__bridge_retained CFDictionaryRef)keychainQuery);
//Add new object to search dictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge_transfer id)kSecValueData];
//Add item to keychain with the search dictionary
SecItemAdd((__bridge_retained CFDictionaryRef)keychainQuery, NULL);
}
+ (id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Configure the search setting
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnData];
[keychainQuery setObject:(__bridge_transfer id)kSecMatchLimitOne forKey:(__bridge_transfer id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((__bridge_retained CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
@try {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge_transfer NSData *)keyData];
} @catch (NSException *e) {
NSLog(@"Unarchive of %@ failed: %@", service, e);
} @finally {
}
}
return ret;
}
+ (void)delete:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
}
@end
代码如下:
@interface WQUserDataManager : NSObject
/**
* @brief 存储密码
*
* @param password 密码内容
*/
+(void)savePassWord:(NSString *)password;
/**
* @brief 读取密码
*
* @return 密码内容
*/
+(id)readPassWord;
/**
* @brief 删除密码数据
*/
+(void)deletePassWord;
@end
代码如下:
#import "WQUserDataManager.h"
@implementation WQUserDataManager
static NSString * const KEY_IN_KEYCHAIN = @"com.wuqian.app.allinfo";
static NSString * const KEY_PASSWORD = @"com.wuqian.app.password";
+(void)savePassWord:(NSString *)password
{
NSMutableDictionary *usernamepasswordKVPairs = [NSMutableDictionary dictionary];
[usernamepasswordKVPairs setObject:password forKey:KEY_PASSWORD];
[WQKeyChain save:KEY_IN_KEYCHAIN data:usernamepasswordKVPairs];
}
+(id)readPassWord
{
NSMutableDictionary *usernamepasswordKVPair = (NSMutableDictionary *)[WQKeyChain load:KEY_IN_KEYCHAIN];
return [usernamepasswordKVPair objectForKey:KEY_PASSWORD];
}
+(void)deletePassWord
{
[WQKeyChain delete:KEY_IN_KEYCHAIN];
}
@end
实现一个简单的界面,把设定的密码存起来,然后立即读取显示出来看看效果
代码如下:
-(IBAction)btnAciton:(id)sender
{
[WQUserDataManager savePassWord:self.textfield.text];
self.label.text = [WQUserDataManager readPassWord];
}