KeyChain
来源:互联网 发布:专利公司 知乎 编辑:程序博客网 时间:2024/06/05 20:11
Keychain 基础
根据苹果的介绍,iOS设备中的Keychain是一个安全的存储容器,可以用来为不同应用保存敏感信息比如用户名,密码,网络密码,认证令牌。苹果自己用keychain来保存Wi-Fi网络密码,VPN凭证等等。它是一个sqlite数据库,位于/private/var/Keychains/keychain-2.db,其保存的所有数据都是加密过的。
开发者通常会希望能够利用操作系统提供的功能来保存凭证(credentials)而不是把它们(凭证)保存到NSUserDefaults,plist文件等地方。保存这些数据的原因是开发者不想用户每次都要登录,因此会把认证信息保存到设备上的某个地方并且在用户再次打开应用的时候用这些数据自动登录。Keychain的信息是存在于每个应用(app)的沙盒之外的。
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使用
但是这里我只需要存取用户名和密码,就不用苹果提供的类了,自己写个简单的类来实现就好。
代码如下:
#import #import @interface CHKeychain : NSObject+ (void)save:(NSString *)service data:(id)data;+ (id)load:(NSString *)service;+ (void)delete:(NSString *)service;@end
CHKeychain.m
#import "CHKeychain.h"@implementation CHKeychain+ (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);}@end
keychain access groups(可跳过)
通过keychain access groups可以在应用之间共享keychain中的数据。要求在保存数据到keychain的时候指定group。把数据保存到keychain的最好方法就是用苹果提供的KeychainItemWrapper。可以到这下载例子工程。第一步就是创建这个类的实例。
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”];
要把信息保存到keychain中,使用 setObject:forKey: 方法。在这里, (id)kSecAttrAccount 是一个预先定义好的键(key),我们可以用它来保存账号名称。 kSecClass指定了我们要保存的某类信息,在这里是一个通用的密码。kSecValueData可以被用来保存任意的数据,在这里是一个密码。
[wrapper setObject:kSecClassGenericPassword forKey:(id)kSecClass];
[wrapper setObject:@"username" forKey:(id)kSecAttrAccount];
[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中导出数据的最流行工具是ptoomey3的Keychain dumper。其github地址位于此。现在到这个地址把它下载下来。然后解压zip文件。在解压的文件夹内,我们感兴趣的文件是keychain_dumper这个二进制文件。一个应用能够访问的keychain数据是通过其entitlements文件指定的。keychain_dumper使用一个自签名文件,带有一个*通配符的entitlments,因此它能够访问keychain中的所有条目。 当然,也有其他方法来使得所有keychain信息都被授权,比如用一个包含所有访问组(access group)的entitlements文件,或者使用一个特定的访问组(access group)使得能够访问所有的keychain数据。 例如,工具Keychain-viewer就使用如下的entitlements.
虽然keychain也容易被破解,不过比NSUserDefaults和plist安全得多,只要我们注意不要在keychain中保存明文密码就会在很大程度上提升安全性。
- KeyChain
- keychain
- keychain
- KeyChain
- keychain
- Keychain
- Keychain
- keychain
- KeyChain
- Keychain
- keychain
- keychain
- keychain
- ios keyChain
- keychain 访问
- keychain应用
- IOS KeyChain
- IOS KeyChain
- 【有向图强连通分量(SCC)】
- LoadRunner中winsocket协议脚本回放时的mismatch问题处理方法
- 将VIM打造成强大的IDE
- java入门-枚举类型与for/switch语句
- 关于c#几个事件处理函数能否并行运行的实验
- KeyChain
- Hibernate一级缓存和二级缓存详解
- Oracle,SqlServer,MySql高性能分页方案
- php邮件发送pop3
- Java字节流和字符流的区别
- 蛇形矩阵的各种情形:共4种5类
- OpenGL API 之 glPixelStore
- Factory Method模式
- BaseDao终极版