前言 :
项目中有时会须要存储敏感信息(如密码、密钥等),苹果官方提供了一种存储机制--钥匙串(keychain)
。
keychain是一种存储在硬盘
上的加密的数据库
。这个多是卸载App后,keychain信息还在的缘由。
keychain适合存储较小的数据量
(不超过上千字节或上兆字节
)的内容。
笔者作了一个关于keychain的增、删、改、查
的Demo(QiKeychain),给你们介绍下keychain的基本使用。ios
下图(确保keychain中用户的信息安全)有利于咱们直观了解keychain。 git
![]()
笔者用Demo(QiKeychain)作了4件事。github
- 增长:存储用户名、密码到keychain;
- 查询:根据用户名从keychain中查询密码;
- 删除:从keychain中删除用户名、密码等相应信息;
- 修改:修改keychain中的用户名对应的密码;
Demo(QiKeychain)对keychain的操做效果以下:数据库
- 存储用户名 “QiShare”,密码:1234;
- 查询用户名为“QiShare”的密码,显示密码为:1234;
- 修改用户名“QiShare”的密码为“123456”;
- 查询“QiShare”的密码,显示为“123456”;
- 把“QiShare”从keychain中删除。
keychain有四个经常使用的API,用于增、删、改、查keychain中的数据。
keychain中的数据子项是以item的形式存在的。
举个例子:就存储用户名、密码的情景来讲,每一个item包含存储的用户名和密码及其余属性信息,keychain中包含多个用户名和密码的item。macos
下图(把数据和属性存储到keychain中)利于咱们理解存储过程 安全
![]()
OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef * __nullable CF_RETURNS_RETAINED result)
API_AVAILABLE(macos(10.6), ios(2.0));
复制代码
存储关键代码:bash
NSDictionary *saveSecItems = @{(id)kSecClass: (id)kSecClassGenericPassword,
(id)kSecAttrService: service,
(id)kSecAttrAccount: account,
(id)kSecValueData: passwordData
};
OSStatus saveStatus = SecItemAdd((CFDictionaryRef)saveSecItems, NULL);
复制代码
OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef * __nullable CF_RETURNS_RETAINED result)
API_AVAILABLE(macos(10.6), ios(2.0));
复制代码
查询关键代码:微信
NSDictionary *matchSecItems = @{
(id)kSecClass: (id)kSecClassGenericPassword,
(id)kSecAttrService: service,
(id)kSecAttrAccount: account,
(id)kSecMatchLimit: (id)kSecMatchLimitOne,
(id)kSecReturnData: @(YES)
};
CFTypeRef dataRef = nil;
OSStatus errorCode = SecItemCopyMatching((CFDictionaryRef)matchSecItems, (CFTypeRef *)&dataRef);
复制代码
OSStatus SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
API_AVAILABLE(macos(10.6), ios(2.0));
复制代码
注意:更新代码这部分,笔者开始的时候遇到一些问题,还要多谢组内成员,尤为是昆哥的指教。
SecItemUpdate接收了2个参数,query和attributesToUpdate。
第一个参数query用于查询到相应的item, 第二个参数attributesToUpdate用于传入要更新的信息。
笔者曾错误地给第二个参数attributesToUpdate传入过(id)kSecClass: (id)kSecClassGenericPassword要更改的内容。
结果报错为:
errSecNoSuchAttr = -25303, /* The specified attribute does not exist. */
网络
更新关键代码:
NSDictionary *queryItems = @{(id)kSecClass: (id)kSecClassGenericPassword,
(id)kSecAttrService: service,
(id)kSecAttrAccount: account
};
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *updatedItems = @{
(id)kSecValueData: passwordData,
};
OSStatus updateStatus = SecItemUpdate((CFDictionaryRef)queryItems, (CFDictionaryRef)updatedItems);
复制代码
OSStatus SecItemDelete(CFDictionaryRef query)
API_AVAILABLE(macos(10.6), ios(2.0));
复制代码
删除关键代码:
NSDictionary *deleteSecItems = @{
(id)kSecClass: (id)kSecClassGenericPassword,
(id)kSecAttrService: service,
(id)kSecAttrAccount: account
};
OSStatus errorCode = SecItemDelete((CFDictionaryRef)deleteSecItems);
复制代码
增删改查
相关的API都须要设置相应的属性字典
(分别代指上述的saveSecItems 、matchSecItems 、queryItems 、updatedItems 、deleteSecItems)
- (id)kSecClass: (id)kSecClassGenericPassword kSecClass表示item的class (id)kSecClass的值代表一个通用的密码item笔者通常都传入kSecClassGenericPassword
- (id)kSecAttrService: service kSecAttrService的value用于代表item的service
- (id)kSecAttrAccount: account (id)kSecAttrAccoun的值代表item的账户名
- (id)kSecValueData: passwordData (id)kSecValueData表示item的数据
- (id)kSecMatchLimit: (id)kSecMatchLimitOne, (id)kSecMatchLimit 有2个值(id)kSecMatchLimitOne、和(id)kSecMatchLimitAll kSecMatchLimitOne:表示只匹配第一个符合条件的item kSecMatchLimitAll:表示匹配不限数量的items
- (id)kSecReturnData: @(YES) (id)kSecReturnData的值是一个Boolean类型的值用于肯定是否返回item data
- kSecClass的值表示item的class kSecClass的值代表一个通用的密码item笔者通常都传入的kSecClassGenericPassword
- kSecClass的值表示item的class kSecClass的值代表一个通用的密码item笔者通常都传入的kSecClassGenericPassword
在Demo(QiKeychain)中,笔者对keychain相关使用的API进行了封装。获取Demo(QiKeychain)GitHub地址:QiKeychain。
注意:
笔者后来封装的代码,修改了保存操做的逻辑。
修改内容为:在保存用户名密码的时候
-> 先在keychain中查询
用户名是否存在
-> 若存在,就进行更新
操做;
-> 若不存在就进行保存
操做。
相应示意图(使用钥匙串存储网络密码)以下:
![]()
小编微信:可加并拉入《QiShare技术交流群》。
关注咱们的途径有:
QiShare(简书)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公众号)
推荐文章:
iOS 自定义拖拽式控件:QiDragView
iOS 自定义卡片式控件:QiCardView
iOS Wireshark抓包
iOS Charles抓包
初探TCP
IP、UDP初探
奇舞周刊