通常情况下,我们用NSUserDefaults存储数据信息,但是对于一些私密信息,比如密码、证书等等,就需要使用更为安全的keychain了。keychain里保存的信息不会因App被删除而丢失,在用户重新安装App后依然有效,数据还在。
使用苹果官方发布的KeychainItemWrapper或者SFHFKeychainUtils很方便,后来看到 iphone使用keychain来存取用户名和密码 一文,觉得对了解keychain有很大的帮助,于是ARC控也尝试了一把。
需要导入Security.framework
@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
"
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];
}
使用keychain保存用户名和密码等敏感信息 KeychainItemWrapper和SFHFKeychainUtils。
iOS的keychain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式,每个ios程序都有一个独立的keychain存储。相对于NSUserDefaults、文件保存等一般方式,keychain保存更为安全,而且keychain里保存的信息不会因App被删除而丢失,所以在重装App后,keychain里的数据还能使用。从ios 3。0开始,跨程序分享keychain变得可行。
如何需要在应用里使用使用keyChain,我们需要导入Security.framework ,keychain的操作接口声明在头文件SecItem.h里。直接使用SecItem.h里方法操作keychain,需要写的代码较为复杂,为减轻咱们程序员的开发,我们可以使用一些已经封装好了的工具类,下面我会简单介绍下我用过的两个工具类:KeychainItemWrapper和SFHFKeychainUtils。
KeychainItemWrapper是apple官方例子“GenericKeychain”里一个访问keychain常用操作的封装类,在官网上下载了GenericKeychain项目后,只需要把“KeychainItemWrapper.h”和“KeychainItemWrapper.m”拷贝到我们项目,并导入Security.framework 。KeychainItemWrapper的用法:
/** 初始化一个保存用户帐号的KeychainItemWrapper */
*wrapper
=
[
[KeychainItemWrapper alloc
] initWithIdentifier
:@
"Account Number"
:@
"YOUR_APP_ID_HERE.com.yourcompany.AppIdentifier"
]
;
//保存帐号
[wrapper setObject
:@
"<帐号>" forKey
:
(id
)kSecAttrAccount
]
;
//保存密码
[wrapper setObject
:@
"<帐号密码>" forKey
:
(id
)kSecValueData
]
;
//从keychain里取出帐号密码
*password
=
[wrapper objectForKey
:
(id
)kSecValueData
]
;
//清空设置
[wrapper resetKeychainItem
]
;
其中方法“- (void)setObject:(id)inObject forKey:(id)key;”里参数“forKey”的值应该是Security.framework 里头文件“SecItem.h”里定义好的key,用其他字符串做key程序会崩溃!
SFHFKeychainUtils是另一个封装了KeyChain简单操作的第三方类库,使用上比KeychainItemWrapper要简单点,SFHFKeychainUtils只提供了获取、保存和删除三个方法:
<UIKit
/UIKit.
h
>
interface SFHFKeychainUtils
: NSObject
{
}
/** 从Keychain里获取用户密码
*param username 用户名
*param serviceName 服务名
*return NSString 用户名对应的密码
*/
+
(NSString
*
) getPasswordForUsername
:
(NSString
*
) username andServiceName
:
(NSString
*
) serviceName error
:
(NSError
**
) error
;
/**
*把用户的密码保存到Keychain里
*@param username 用户名
*@param password 要保存的密码
*@param serviceName 本条keychains所属的服务(组)
*return BOOL 是否存储成功
*/
+
(BOOL
) storeUsername
:
(NSString
*
) username andPassword
:
(NSString
*
) password forServiceName
:
(NSString
*
) serviceName updateExisting
:
(BOOL
) updateExisting error
:
(NSError
**
) error
;
/**
* 删除某个用户信息
*param username 用户名
*param serviceName 用户所属的服务(组)
*return BOOL 是否删除成功
*/
+
(BOOL
) deleteItemForUsername
:
(NSString
*
) username andServiceName
:
(NSString
*
) serviceName error
:
(NSError
**
) error
;
@end
使用方法:
"com.mycompany.yourAppServiceName"
*error
;
*userName
= @
"<用户名>"
;
*password
= @
"<用户密码>"
;
/** 保存用户的密码*/
=
[SFHFKeychainUtils storeUsername
:userName
:password
:ServiceName
:YES
:&error
]
;
if
(
!saved
)
{
(@
"保存密码时出错:%@", error
)
;
}
= nil
;