iOS 之keychain详解(附有Demo)
iOS keychain是苹果用来保存用户私密数据的一个专业的SQLite数据库。保存的数据主要是一些轻量级的私密数据,比如用户密码,token(令牌)等,保存在这个数据库中的密码不会因为你卸载了app就不见了,只要你重新安装app。调用相关的服务、账户API就能得到app上次保存在数据库中的密码。这对于用户卸载app之后又重新安装但是却忘记了密码的情况很适用。
有一点很奇怪,经测试,不同的开发者账号访问同一服务同一账号下的密钥的时候竟然都能获取得到,这样岂不是会有冲突。希望有知道原因的小伙伴们在评论中留言,解答一下俺心中的疑问╮(~▽~)╭
iOS keychain共有5种类型: kSecClassGenericPassword、kSecClassInternetPassword、kSecClassCertificate、kSecClassKey、kSecClassIdentity
这五种类型分别有不同的属性,你可以理解为数据中5个不同的表,他们有各自对应的字段,因此对应的它们的增删改查其实有点类似于sql语句的操作,下面是不同类型对应的属性:
现在以kSecClassGenericPassword(一般的密码)为例:
增加keychain
//新增keychain
- (void)addKeychainPassword{
NSDictionary *query = @{(__bridge id)kSecAttrAccessible:(__bridge id)kSecAttrAccessibleWhenUnlocked,(__bridge id)kSecClass:(__bridge id)kSecClassGenericPassword,(__bridge id)kSecValueData:[@"" dataUsingEncoding:NSUTF8StringEncoding],(__bridge id)kSecAttrAccount:@"account name",(__bridge id)kSecAttrService:@"loginPassword"};
/*
参数一:
kSecAttrAccessibleWhenUnlocked 表示获取当前密钥只要屏幕处于解锁状态就可以了
kSecAttrAccessibleAfterFirstUnlock 表示手机第一次解锁就可以获取当前密钥
kSecAttrAccessibleAlways 表示任何时候都可以获取当前密钥
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly 表示获取密钥只能在当前设备,把手机数据恢复到新的手机中是不可用的
kSecAttrAccessibleWhenUnlockedThisDeviceOnly 非锁定状态,且设备唯一指定,同上
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly 第一次解锁,且设备唯一指定,同上
kSecAttrAccessibleAlwaysThisDeviceOnly 总是可以获取,当然也是设备唯一指定,同上
参数二:
kSecClassGenericPassword 为keychain类型
参数三:
kSecValueData 存储的数据,就是密码、token存储的地方,要转化为NSData类型
参数四:
kSecAttrAccount 为账户名 作为账户密码的唯一索引
参数五:
kSecAttrService 为服务名 作为账户密码的唯一索引
*/ CFTypeRef result;
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, &result); if (status == errSecSuccess) {
NSLog(@"添加成功");
}else{
NSLog(@"添加失败");
}
}
查询keychain:
//查询keychain
- (void)queryKeychainPassword{
NSDictionary *query = @{(__bridge id)kSecClass:(__bridge id)kSecClassGenericPassword,
(__bridge id)kSecReturnData:@YES,
(__bridge id)kSecMatchLimit:(__bridge id)kSecMatchLimitOne,
(__bridge id)kSecAttrAccount:@"account name",
(__bridge id)kSecAttrService:@"loginPassword"};
//kSecMatchLimitOne 表示查询返回一条记录,有可能查到多条记录,一般默认返回一条记录
//kSecMatchLimitAll 表示返回所有记录
//SecItemCopyMatching函数会根据query里面的查询条件查找对应符合要求的记录,另外根据不同的keychain类型dataTypeRef会返回对应的不同类型如NSArray、NSDictionary、NSData CFTypeRef dataTypeRef = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataTypeRef);
if (status == errSecSuccess) {
NSString *pwd = [[NSString alloc]initWithData:(__bridge NSData*)dataTypeRef encoding:NSUTF8StringEncoding];
NSLog(@"pwd:%@",pwd);
}
}
查询keychain对应的属性:
//另外可以通过kSecReturnRef查询其他属性,相对于前面的返回密钥的引用,kSecReturnRef返回的是keychain所有的属性
- (void)queryMoreAttribute{
NSDictionary *query = @{(__bridge id)kSecClass:(__bridge id)kSecClassGenericPassword,
(__bridge id)kSecReturnRef:@YES,
(__bridge id)kSecReturnData:@YES,
(__bridge id)kSecMatchLimit:(__bridge id)kSecMatchLimitOne,
(__bridge id)kSecAttrAccount:@"account name",
(__bridge id)kSecAttrService:@"loginPassword"};
CFTypeRef dataTypeRef = NULL;
//重点是(__bridge id)kSecReturnRef:@YES,声明返回的数据是整个keychain的所有属性 OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataTypeRef); if (status == errSecSuccess) {
NSDictionary *dict = (__bridge NSDictionary *)dataTypeRef;
NSString *acccount = dict[(id)kSecAttrAccount];
NSLog(@"acccount:%@",acccount);
NSData *data = dict[(id)kSecValueData];
NSString *pwd = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"pwd:%@",pwd);
NSString *service = dict[(id)kSecAttrService];
NSLog(@"service==result:%@", service);
}
}
更新keychain:
//修改keychain
- (void)changeKeychainPassword{
NSDictionary *query = @{(__bridge id)kSecClass:(__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService:@"loginPassword",
(__bridge id)kSecAttrAccount:@"account name"};
NSDictionary *update = @{(__bridge id)kSecValueData:[@"" dataUsingEncoding:NSUTF8StringEncoding]}; OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update); if (status == errSecSuccess) {
NSLog(@"更新成功");
}
}
删除keychain:
//删除keychain
- (void)deleteKeychainPassword{
NSDictionary *query = @{
(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService : @"loginPassword",
(__bridge id)kSecAttrAccount : @"account name"
}; //尽量详细的添加多个属性,避免误删其他keychain OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); if (status == errSecSuccess) {
NSLog(@"成功删除");
}
}
iOS keychain有一个特色功能就是keychainsharing,它能实现多个同一个开发者账号下的多个应用共享keychain,前提是要开启keychainsharing功能,如下图所示:
可以在keychainsharing中对应的分组,把keychain添加的到分组的操作可以这样写
//sharing Items
//添加sharing Items
- (void)addSharingItems{
NSDictionary *query = @{(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlocked,
(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecValueData : [@"" dataUsingEncoding:NSUTF8StringEncoding],
(__bridge id)kSecAttrAccount : @"account name",
(__bridge id)kSecAttrAccessGroup : @"5Q8QKERR7H.com.mycom.iOS-keychain",
(__bridge id)kSecAttrService : @"loginPassword",
(__bridge id)kSecAttrSynchronizable : @YES,
};
/*
(__bridge id)kSecAttrSynchronizable : @YES 表示可以同步到icloud,如果要同步到其他设备,请注意避免使用DeviceOnly设置等其他和设备唯一性相关的设置
*/
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, nil); if (status == errSecSuccess) {
NSLog(@"sharing Items添加成功");
}else{
NSLog(@"sharing Items添加失败");
}
}
查询对应的分组
//查询sharing Items
- (void)querySharingItems{
NSDictionary *query = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecReturnRef : @YES,
(__bridge id)kSecReturnData : @YES,
(__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitAll,
(__bridge id)kSecAttrAccount : @"account name",
(__bridge id)kSecAttrAccessGroup : @"5Q8QKERR7H.com.mycom.iOS-keychain",
(__bridge id)kSecAttrService : @"loginPassword",
}; CFTypeRef dataTypeRef = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataTypeRef); if (status == errSecSuccess) {
NSLog(@"sharing Items查询成功");
}else{
NSLog(@"sharing Items查询失败");
}
}
和对单个keychain进行操作不同的是添加多了一个kSecAttrAccessGroup属性,用于指明对应的分组,这里要注意的是要添加开发者账号的teamId,用于区分不同的开发者。
SSKeychain框架 github地址:https://github.com/Mingriweiji-github/sskeychain-master
如果都是自己定义属性进行增删改查的操作,是比较容易出bug的,而且操作也比较繁琐,毕竟这些操作都是基于C语言的API操作。下面介绍一个简单的轻量级框架SSKeychain来实现我们对密钥的增删改查,闲话少说,直接上代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib. // [SSKeychain setAccessibilityType:kSecAttrAccessibleWhenUnlocked]; //设置访问权限,不设置则按照默认权限(这个我看源码没有显示默认的是什么,不知道keychain默认保存的权限是不是kSecAttrAccessibleAlways) [SSKeychain setPassword:@"" forService:kKeyChainSaveAccountService account:kKeyChainSaveAccount]; //设置密钥
[SSKeychain setPassword:@"" forService:kKeyChainSaveAccountService account:kKeyChainSaveAccount1]; //设置密钥
NSError *error;
NSString *password = [SSKeychain passwordForService:kKeyChainSaveAccountService account:kKeyChainSaveAccount error:&error]; //获取密钥
NSLog(@"password:%@,error:%@",password,error); NSArray *counts = [SSKeychain accountsForService:kKeyChainSaveAccountService error:&error];
NSLog(@"counts:%@",counts); //获取服务下相关账户所有的属性
NSError *error1;
[SSKeychain deletePasswordForService:kKeyChainSaveAccountService account:kKeyChainSaveAccount error:&error1];
if (error1) {
NSLog(@"删除失败:%@",error1);
}else{
NSLog(@"删除成功");
}
}
最后附上本文Demo:https://github.com/LuPing-Kuang/iOS-keychain
iOS 之keychain详解(附有Demo)的更多相关文章
- 了解iOS消息推送一文就够:史上最全iOS Push技术详解
本文作者:陈裕发, 腾讯系统测试工程师,由腾讯WeTest整理发表. 1.引言 开发iOS系统中的Push推送,通常有以下3种情况: 1)在线Push:比如QQ.微信等IM界面处于前台时,聊天消息和指 ...
- iOS开发者证书-详解
iOS开发者证书-详解/生成/使用 本文假设你已经有一些基本的Xcode开发经验, 并注册了iOS开发者账号. 相关基础 加密算法 现代密码学中, 主要有两种加密算法: 对称密钥加密 和 公开密钥加密 ...
- 转载]IOS LBS功能详解[0](获取经纬度)[1](获取当前地理位置文本 )
原文地址:IOS LBS功能详解[0](获取经纬度)[1](获取当前地理位置文本作者:佐佐木小次郎 因为最近项目上要用有关LBS的功能.于是我便做一下预研. 一般说来LBS功能一般分为两块:一块是地理 ...
- iOS中-Qutarz2D详解及使用
在iOS中Qutarz2D 详解及使用 (一)初识 介绍 Quartz 2D是二维绘图引擎. 能完成的工作有: 绘制图形 : 线条\三角形\矩形\圆\弧等 绘制文字 绘制\生成图片(图像) 读取\生成 ...
- iOS 2D绘图详解(Quartz 2D)之路径(点,直线,虚线,曲线,圆弧,椭圆,矩形)
前言:一个路径可以包含由一个或者多个shape以及子路径subpath,quartz提供了很多方便的shape可以直接调用.例如:point,line,Arc(圆弧),Curves(曲线),Ellip ...
- iOS开发——Block详解
iOS开发--Block详解 1. Block是什么 代码块 匿名函数 闭包--能够读取其他函数内部变量的函数 函数变量 实现基于指针和函数指针 实现回调的机制 Block是一个非常有特色的语法,它可 ...
- iOS开发:详解Objective-C runTime
Objective-C总Runtime的那点事儿(一)消息机制 最近在找工作,Objective-C中的Runtime是经常被问到的一个问题,几乎是面试大公司必问的一个问题.当然还有一些其他问题也几乎 ...
- iOS应用开发详解
<iOS应用开发详解> 基本信息 作者: 郭宏志 出版社:电子工业出版社 ISBN:9787121207075 上架时间:2013-6-28 出版日期:2013 年7月 开本:16开 ...
- laravel5.5的任务调度(定时任务)详解(demo)
https://blog.csdn.net/LJFPHP/article/details/80417552 laravel5.5的定时任务详解(demo) 这篇文章写得挺详细的.看了它我基本就会用了 ...
随机推荐
- 实验吧Web-易-简单的sql注入之3(报错的sql盲注之exp)
题目提示是报错注入,于是就用盲注技巧来注入. 这里注入时发现floor,extractvalue,updatexml被吃掉了,用exp可以注入成功.(记住大小写绕过等技巧) 1.爆库 ' or exp ...
- Java版本和功能指南
您可以使用本指南查找和安装最新的Java,了解Java发行版(AdoptOpenJdk,OpenJDK,OracleJDK等)之间的差异,以及获得Java语言功能的概述,包括Java版本8-13. J ...
- 报错:Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
1.问题 写了一个简单的单层神经网络跑mnist手写数字集,结果每次fit都会出现dead kernel 很多dead kernel首先不要急着去网上搜dead kernel怎么解决,因为大家出现的原 ...
- Ubuntu使用小技巧汇总
1. Ubuntu创建/切换root用户 sudo passwd rootsu root 2. 解决Ubuntu14.04系统没有自带右键打开终端的问题 sudo apt-get install na ...
- 小白学习之pytorch框架(7)之实战Kaggle比赛:房价预测(K折交叉验证、*args、**kwargs)
本篇博客代码来自于<动手学深度学习>pytorch版,也是代码较多,解释较少的一篇.不过好多方法在我以前的博客都有提,所以这次没提.还有一个原因是,这篇博客的代码,只要好好看看肯定能看懂( ...
- Python说文解字_Python之多任务_05
问:在Py3.5之前yield表现非常好,在Py3.5之后为了将予以变得更加明确,就引入了async和await关键词用于定义原生的协议. 答:async和await原生协程: async def d ...
- C/C++学习笔记-gcc动态库及静态库
gcc工作流程 1.预处理 gcc -E 2.编译 gcc -S 3.汇编 gcc -c 4.链接 没参数制作静态库: 1.命名规则:libMyName.a2.制作步骤: ①.生成.o gcc -c ...
- P(Y=y|x;θ)表示什么意思
https://blog.csdn.net/jh1137921986/article/details/88999539 在机器学习中,特别是学习到关于概率/似然估计方面的内容,经常看到类似P(Y=y| ...
- 72)MFC测试动态共享库
动态共享库: 首先我建立一个新的动态库: 然后不选择空项目了,因为我们普通的cpp文件 入口是main win32入口是winmain 那么这个动态库的入口在哪里 我们就是为了看一看: 出来这样 ...
- SQL优化——ORACLE
SQL优化——ORACLE 索引是由Oracle维护的可选结构,为数据提供快速的访问.准确地判断在什么地方需要使用索引是困难的,使用索引有利于调节检索速度. 当建立一个索引时,必须指定用于跟踪的表名以 ...