文档章节

iOS Keychain(钥匙串) 原理和APP之间共享信息(例如:账号密码)

她吃西红柿
 她吃西红柿
发布于 2016/12/02 11:30
字数 1122
阅读 2691
收藏 12

Keychain 介绍

英文好的点这官方文档

Keychain Services 是 macOS 和 iOS 都提供一种安全地存储敏感信息的工具,比如,网络密码:用于保存访问服务器或者网站,通用密码:用来保存应用程序或者数据库密码.与此同时,用于认证的证书,密钥,和身份信息,也可以存储在Keychain中.Keychain Services 的安全机制保证了存储这些敏感信息不会被窃取。简单说来,Keychain 就是一个安全容器。 PS:在iOS中keychian 依赖用于签名的provisioning profile描述文件,确保发布不同版本的时候使用同一个pp文件

输入图片说明

Keychain 的结构

Keychain 可以包含任意数量的 keychain item。每一个 keychain item 包含数据和一组属性。对于一个需要保护的 keychain item,比如密码或者私钥(用于加密或者解密的string字节)数据是加密的,会被 keychain 保护起来的;对于无需保护的 keychain item,例如,证书,数据未被加密。

跟keychain item有关系的取决于item的类型;应用程序中最常用的是网络密码(Internet passwrods)和普通的密码。正如你所想的,网络密码像安全域(security domain)、协议、和路径等一些属性。在macOS中,当keychain被锁的时候加密的item没办法访问,如果你想要该问被锁的item,就会弹出一个对话框,需要你输入对应keychain的密码。当然,未有密码的keychain你可以随时访问。但在iOS中,你只可以访问你自已的keychain items;

Keychain的特点

  • 数据并不存放在App的Sanbox中,即使删除了App,资料依然保存在keychain中。如果重新安装了app,还可以从keychain获取数据。

  • keychain的数据可以用过group方式,让程序可以在App间共享。不过得要相同TeamID

  • keychain的数据是经过加密的 ##Keychain的使用 大多数iOS应用需要用到Keychain, 都用来添加一个密码,修改一个已存在Keychain item或者取回密码。Keychain提供了以下的操作

  • SecItemAdd 添加一个item

  • SecItemUpdate 更新已存在的item

  • SecItemCopyMatching 搜索一个已存在的item

  • SecItemDelete 删除一个keychain item

####读取

 func readPassword() throws -> String  {
        /*
            查找 对应 service, account and
            access group 的keychainItem.
        */
        var query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup)
        query[kSecMatchLimit as String] = kSecMatchLimitOne
        query[kSecReturnAttributes as String] = kCFBooleanTrue
        query[kSecReturnData as String] = kCFBooleanTrue
        
        // 查找已经存在的item
        var queryResult: AnyObject?
        let status = withUnsafeMutablePointer(to: &queryResult) {
            SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
        }
        
        // 检查插入或更新是否成功
        guard status != errSecItemNotFound else { throw KeychainError.noPassword }
        guard status == noErr else { throw KeychainError.unhandledError(status: status) }
        
        // 读取item中的密码信息
        guard let existingItem = queryResult as? [String : AnyObject],
            let passwordData = existingItem[kSecValueData as String] as? Data,
            let password = String(data: passwordData, encoding: String.Encoding.utf8)
        else {
            throw KeychainError.unexpectedPasswordData
        }
        
        return password
    }

####写入

func savePassword(_ password: String) throws {
        // 编码Encoding->data
        let encodedPassword = password.data(using: String.Encoding.utf8)!
        
        do {
            // 查找是否存在item.
            try _ = readPassword()

            // 如果存在更新item中的密码
            var attributesToUpdate = [String : AnyObject]()
            attributesToUpdate[kSecValueData as String] = encodedPassword as AnyObject?

            let query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup)
            let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary)
            
          
            guard status == noErr else { throw KeychainError.unhandledError(status: status) }
        }
        catch KeychainError.noPassword {
            /*
                如果不存在 创建一个dictionary 作为item 存入keychain
            */
            var newItem = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup)
            newItem[kSecValueData as String] = encodedPassword as AnyObject?
   
            let status = SecItemAdd(newItem as CFDictionary, nil)
            
            guard status == noErr else { throw KeychainError.unhandledError(status: status) }
        }
    }

##关于共享 Keychain的数据可以透过Group Access的方式,让资料可以在App间共享,Google系列的App (Gmail、Google+、日历…)就是通过这样的方式来记录使用者登入信息,只要使用者在其中一个App中完成登入了,其他的App也可以读取到同相的登入咨询进行登录。 ###进入Capabilities,将Keychain打开 输入图片说明

开启Keychain后,会自动新增一个Keychain Group,使用的是Bundle Identifier。 同时也会自动新增一个entitlements文件,里面也会有一个Access Group,名为 $(AppIdentifierPrefix)+你的bundleID 输入图片说明

(AppIdentifierPrefix)可以是开发者的代号需要登录才会有,也就是开发者证书后小括号的内的英文数字组合。使用$(AppIdentifierPrefix)只能被同一个开发者账号的app来存取,以防被有心人盜取。 输入图片说明

若其他的App也要存取当前Keychain的数据,就必需在Keychain开启后,新增相同的Keychain Group (Access Group会根据Keychain Group自动新增)。 PS: 需要Info.plist新增一对键值 Key: AppIdentifierPrefix Value: $(AppIdentifierPrefix) 方便取得 Identifier Prefix

##最终效果 输入图片说明 输入图片说明

官方sampleCode

© 著作权归作者所有

她吃西红柿
粉丝 43
博文 52
码字总数 25812
作品 0
杭州
iOS工程师
私信 提问
【读书笔记】iOS-使用钥匙串保护数据

一,将应用从设备上删除时,并不会删除其钥匙串项,这使得调试工作困难得多。模拟器有一个Reset Contents and Settings选项,可用于将钥匙串项移除。因此,强烈建议在模拟器上确定Keychain应...

菜鸟and小白
2018/12/14
0
0
iOS APP与APP之间的通信方式

1、URL Scheme 这个最常见了,基本接入分享、支付、地图的,都会用到 2、UIPasteboard 其实就是粘贴板,常见的就是淘口令啥的,也挺常见的 3、Keychain 这个就是一个安全的存储容器,本质其实...

RainOrz
02/26
58
0
WaxSealCore 2.0.1 释出,Mac“钥匙串”服务编程接口

这是一个 bug 修复版本,可以在这里获取。 更新内容: 修复了钥匙串对象(WSCKeychain)的 URL 属性的百分号(percent escape)编码问题 什么是 WaxSealCore WaxSealCore 是一个受 Cocoa 设计...

开源中国真理部部长
2015/03/28
656
1
UUID、UDID、Keychain

一、UDID 自从iOS5之后,苹果就禁止了通过代码访问UDID,在这之前,可以使用[[UIDevice cuurrent] uniqueIdenfier] 这个方法来获取某设备UDID,现在是不可能了(因为UDID是设备的唯一标识符,...

海耐射手
2018/05/30
0
0
WaxSealCore 1.01 释出,Mac 下的“钥匙串”服务编程接口

WaxSealCore 1.01 释出,改进内容包括: SupportsisInvisibleproperty for passphrase items SupportsisNegativeproperty for passphrase items Bug fixes 什么是 WaxSealCore WaxSealCore ......

开源中国真理部部长
2015/03/05
1K
1

没有更多内容

加载失败,请刷新页面

加载更多

js如何控制table中的某一行动态置顶

两行代码搞定: $('#'+item.roadCode).fadeOut().fadeIn();//获取到需要置顶的行 $(".table").prepend($('#'+item.roadCode)); 其中,fadeOut()方法 作用 --- 从可见到隐藏 如下: prepend(......

码妞
今天
4
0
四种解决Nginx出现403 forbidden 报错的方法

我是在在本地用虚拟机中通过yum安装nginx的,安装一切正常,但是访问时报403, 于是查看nginx日志,路径为/var/log/nginx/error.log。打开日志发现报错Permission denied,详细报错如下: 1....

dragon_tech
今天
3
0
获取RestResultResponse返回的值

Springboot项目,需要调其他服务的接口,返回值类型是RestResultResponse 打断点的结果集是这个 打印出来的getData(): [{id=3336b624-8474-4dd9-bd5b-c7358687c877, paraNo=104, para=Postpo...

栾小糖
今天
4
0
【小学】 生成10以内的加减法

#!/usr/bin/env python# coding: utf-8from random import randrange# 题目的最大数值R_MAX = 10# 生成的题目的数量R_PAGE = 70# 生成减法列表def get_sub_list():...

Tensor丨思悟
今天
11
0
JavaScript设计模式——适配器模式

  适配器模式是设计模式行为型模式中的一种模式;   定义:   适配器用来解决两个已有接口之间不匹配的问题,它并不需要考虑接口是如何实现,也不用考虑将来该如何修改;适配器不需要修...

有梦想的咸鱼前端
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部