博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
IOS keychain 使用
阅读量:2346 次
发布时间:2019-05-10

本文共 5961 字,大约阅读时间需要 19 分钟。

首先使用keychain 要先导入 Security.framework 这个系统framework,然后在文件里 import 头文件,由于这个框架是c语言风格的接口API,会让人很疑惑,如果想快速集成,推荐这篇,如果想弄明白整个API 下面详细讲解

Keychain概览Keychain里可以存储若干条目(item),每个条目都属于某一个类别(class),以下是常见的几种类别:

  • kSecClassInternetPassword 属于该类别的条目往往用来存储上网登录密码,远程服务器密码等
  • kSecClassGenericPassword存储一些通用的密码,比如数据库密码,vpn连接的密码等等
  • kSecClassCertificatekSecClassKeykSecClassIdentity这三类条目往往用于建立基于证书,秘钥和公钥系统的安全连接。

条目类别是一个条目最基本的属性,每个存入keychain的条目,都需要为它制定一个类别。

操作keychain常见的3个方法:

  • SecItemAdd 添加新条目到keychain.
  • SecItemUpdate修改一个已存在keychain里的条目
  • SecItemCopyMatching查询keychain里的条目并且读取条目信息

下面用一张摘取自苹果官方网站的一张图阐述一下一个常见keychain的操作流程,该图以一个应用连接ftp服务器为例

在这里插入图片描述

在上图中,应用大概流程是这样的:应用开始连接ftp服务器,首先查询keychain是否存在服务器密码,如果存在,那么直接取出密码登陆;如果不存在,那么应用弹出对话框要求用户输入,然后进行登陆,如果登陆成功,那么存储密码到keychain。简单来说,如果要存储一个password,需要先遍历Keychain,看它的password是否已经存在于Keychain中,存在的话就更新它的值,不存在就存储。所以这就使用到苹果提供的方法:

// 查询OSStatus SecItemCopyMatching(CFDictionaryRefquery,CFTypeRef*result);// 添加OSStatus SecItemAdd(CFDictionaryRefattributes,CFTypeRef*result);// 更新OSStatus SecItemUpdate(CFDictionaryRefquery,CFDictionaryRefattributesToUpdate);// 删除OSStatus SecItemDelete(CFDictionaryRefquery)

接下来看看操作Keychain常用的key-value.kSecClass:有五个值,分别为

  • kSecClassGenericPassword(通用密码--也是接下来使用的)、
  • kSecClassInternetPassword(互联网密码)
  • kSecClassCertificate(证书)
  • kSecClassKey(密钥)
  • kSecClassIdentity(身份)
  • kSecAttrService:服务
  • kSecAttrServer:服务器域名或IP地址
  • kSecAttrAccount:账号
  • kSecAttrAccessGroup:可以在应用之间共享keychain中的数据
  • kSecMatchLimit:返回搜索结果
  • kSecMatchLimitOne 一个
  • kSecMatchLimitAll(全部)

Keychain接口参数所有对keychain接口的操作,参数的传递基本上都用到字典,将所需要的参数放入字典,然后将字典传递给keychain接口。

上面提到的条目类别,就是一个参数,除此之外,操作条目往往还需要条目ID,条目所属服务名,条目所属账户名等。这些参数的名称如下:kSecClass:条目类别kSecAttrGeneric:条目idkSecAttrService:条目所属服务kSecAttrAccount:条目所属账户名其中kSecAttrService和kSecAttrAccount在整个keychain里必须唯一,不能重名。
Keychain条目查询使用SecItemCopyMatching进行查询,查询时,我们需要指明要查询条目的类别(kSecClass),条目的id(kSecAttrGeneric),条目所属的服务和账户(kSecAttrService,kSecAttrAccount)。此外,我们还可以设置其他一些查询条件,比如返回条目的数量(kSecMatchLimit),返回条目的数据类型,比如:
kSecReturnData:返回条目所存储的数据,返回值类型是CFDataRef
kSecReturnAttributes:返回该条目的属性,返回值是字典类型CFDictionaryRefk
SecReturnRef:返回条目的引用,根据条目所属类别,返回值类型可能是:SecKeychainItemRef, SecKeyRef,SecCertificateRef, SecIdentityRef.
kSecReturnPersistentRef:返回条目的引用,返回值类型是CFDataRef
如下代码示例用来查询存储在keychain里的密码

//建立词典,用来传递参数NSMutableDictionary *dictionary = [[NSMutableDictionary dictionary];//设置条目类别,我们用该条目存储普通密码,所以设置成kSecClassGenericPassword[dictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];//设置条目的id,比如“MyPasswordForFtp",条目id必须时NSDate,而不是NSStringNSString *itemIDString = @"MyPasswordForFtp";NSData *itemID= [itemIDString dataUsingEncoding:NSUTF8StringEncoding];[dictionary setObject:itemIDforKey:(id)kSecAttrGeneric];//设置条目所属的服务和账户,为了避免重名,我们使用常见的反转域名规则,比如com.mykeychain.ftppasswordNSString *account = @"com.mykeychain.ftppassword";NSString *service = @"com.mykeychain.ftppassword";[dictionary setObject:account forKey:(id)kSecAttrAccount];[dictionary setObject:service forKey:(id)kSecAttrService];//设置查询条件,只返回一个条目[dictionary setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];//设置查询条件,返回条目存储的数据 (kSecReturnData == True)[dictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];//开始查询NSData *result = nil;OSStatus status = SecItemCopyMatching((CFDictionaryRef)dictionary , (CFTypeRef *)&result);

Keychain条目添加使用SecItemAdd()进行条目添加,我们需要指明新条目的类别(kSecClass),新条目的id(kSecAttrGeneric),新条目所属的服务和账户(kSecAttrService,kSecAttrAccount),此外,还需要指明该条目所存储的数据(kSecValueData),否则,没有数据的条目就没有任何存入keychain的意义了。请看如下代码示例:

//建立词典,用来传递参数NSMutableDictionary *dictionary = [[NSMutableDictionary dictionary];//设置条目类别,我们用该条目存储普通密码,所以设置成 kSecClassGenericPassword[dictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];//设置条目的id,比如“MyPasswordForFtp",条目id必须时NSDate,而不是NSStringNSString *itemIDString = @"MyPasswordForFtp";NSData *itemID= [itemIDString dataUsingEncoding:NSUTF8StringEncoding];[dictionary setObject:itemIDforKey:(id)kSecAttrGeneric];//设置条目所属的服务和账户,为了避免重名,我们使用常见的反转域名规则,比如com.mykeychain.ftppasswordNSString *account = @"com.mykeychain.ftppassword";NSString *service = @"com.mykeychain.ftppassword";[dictionary setObject:account forKey:(id)kSecAttrAccount];[dictionary setObject:service forKey:(id)kSecAttrService];//设置条目数据,条目数据时NSDateNSString *password = @"123456";NSData *itemData = [password dataUsingEncoding:NSUTF8StringEncoding];[dictionary setObject:itemData forKey:(id)kSecValueData];//添加条目到keychainOSStatus status = SecItemAdd((CFDictionaryRef)dictionary, NULL);

keychain条目的修改使用SecItemUpdate()进行条目修改,需要传入两个词典,第一个词典用来查询条目,第二个词典用来传递修改后的新值。查询条目所用的词典设置请看上述Keychain条目查询章节,更新值所用词典里设置要更新的参数和值。这里请注意的是,查询词典里不能设置任何查询条件,比如kSecMatchLimit和kSecReturnData,kSecReturnAttributes等,否则会出错。因为这里的查询是为了更新数据,是以写入为目的,不是真正的查询和读取条目,所以设置这些和读取有关的条件是无意义的,系统会认为是和安全有关的错误,会返回-50(errSecParam)下面代码用来更新密码,要更新的条目id为“MyPasswordForFtp”的条目

//建立查询字典

NSMutableDictionary *searchDictionary = [[NSMutableDictionary dictionary];
//设置查询字典
[searchDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
NSString *itemIDString = @“MyPasswordForFtp”;NSData *itemID= [itemIDString dataUsingEncoding:NSUTF8StringEncoding];
[searchDictionary setObject:itemIDforKey:(id)kSecAttrGeneric];
NSString *account = @“com.mykeychain.ftppassword”;NSString *service = @“com.mykeychain.ftppassword”;
[searchDictionary setObject:account forKey:(id)kSecAttrAccount];
[searchDictionary setObject:service forKey:(id)kSecAttrService];
// 建立更新字典
NSMutableDictionary updateDictionary = [[NSMutableDictionary dictionary];
// 设置要更新的新密码
NSString
newPassword = @“654321”;
NSData *passwordData = [newPassword dataUsingEncoding:NSUTF8StringEncoding]; [updateDictionary setObject:passwordData forKey:(id)kSecValueData];
//进行更新
OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictionary, (CFDictionaryRef)updateDictionary);

keychain接口常见错误SecItemUpdate 返回-50(errSecParam):请检查查询词典里是否存在读取条件,比如kSecReturnData,kSecMatchLimit等SecItemAdd 返回-25299 (errSecDuplicateItem: 请检查kSecAttrAccount和kSecAttrService是否已经存在于keychain中,请尝试设置其他值避免重复官方文档:

作者:X堇色

链接:
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

你可能感兴趣的文章
大学生如何学好Java?过来人给你7点建议
查看>>
过来人的经验:Java实习生在公司里一般都做什么?
查看>>
10个学习Java的网站,肯定有你不知道的哦!
查看>>
Java入门基础知识点整理大放送,推荐收藏
查看>>
如何成为月入25k的高级程序员?建议养成这7个习惯
查看>>
学 Java还是Python, 哪个更好找工作?
查看>>
Java基础知识:如何计算Java对象占用内存大小?
查看>>
月薪2W的Java开发工程师,需要掌握哪些技能点?
查看>>
java架构师需要学什么?一个月收入是多少?
查看>>
一份Java程序员的珍藏书单,请您注意查收
查看>>
全宇宙上最全Java基础知识点归纳(建议收藏)
查看>>
这6类人,最适合做程序员!
查看>>
Java入门基础知识点需要学什么?
查看>>
为什么现在Java程序员要求高?看完这些原因你就明白了!
查看>>
有哪些值得推荐的 Java 练手项目?
查看>>
一文看清:Java的核心技术是什么?
查看>>
这10款适合Java开发人员使用的工具,你都掌握了吗?
查看>>
谈谈:如何由浅入深的学习一门编程语言?
查看>>
常用IDEA快捷键大全,你用过哪些?
查看>>
12大理由告诉你选择Java不会后悔!
查看>>