原创blog,转载请注明出处
blog.csdn.net/hello_hwc?viewmode=list
前言:前两篇持久化分别讲到了
本文讲解如何保存需要加密的信息。绝大多数情况下都是保存密码。少数情况下需要保存证书等信息。本文以密码为例,讲解如何用iOS SDK原生API来进行KeyChain的操作。
实际开发的过程中,建议使用一些Github的集成库,或者自己写一个KeyChain的库,很简单
源代码提供Swift版本,完整工程下载
CSDN下载
http://download.csdn.net/detail/hello_hwc/8663811
GitHub
https://github.com/wenchenhuang/SwiftKeyChainDemo
Demo效果
四个按键对应添加,更新,获取,删除
Demo的password没有显示黑点,是为了方便查看。
四种操作
---Get----
KeyChain简介
KeyChain是一个加密的容器,通常用来保存密码,证书,和一些需要加密的key。对于iOS来说,每个App有独立的keyChain,每个app只能访问自己的keyChain.
注意:keyChain的访问权限依赖于provisioning file。所以,如果要在应用更新的时候,仍然能够访问之前保存的密码,要保证provisioning file是同一个文件。
KeyChain描述
keyChain是通过字典来描述的,是一组key-value的对。用来描述这个keyChain是为什么样的应用保存什么样的数据,有什么样的访问权限等等。
一个典型的字典
其中
- kSecClass 表示存储的是密码
- kSecAttrAccount 表示的是为IamUser这个账号存储的密码
- kSecAttrService 表示是为App Store存储的账号
- 其余两个在查询的时候使用,知道如果要查询都设为ture就可以了
所有的keys可以从以下链接获取
https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/index.html
手把手教你建立Demo App
创建一个基于Swift的工程,然后在storyboard上拖拽控件
并且拖拽outlet和action,然后实现UITextFieldDelegate,保证我们点击Return的时候,键盘会消失。这时候的代码如下
import Security
class ViewController: UIViewController,UITextFieldDelegate{
@IBOutlet weak var usernameTextfield: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBAction func addKeyChainItem(sender: AnyObject) {
}
@IBAction func updateKeyChainItem(sender: AnyObject) {
}
@IBAction func getKeyChainItem(sender: AnyObject) {
}
@IBAction func deleteKeyChainItem(sender: AnyObject) {
}
override func viewDidLoad() {
super.viewDidLoad()
usernameTextfield.delegate = self
passwordTextField.delegate = self
// Do any additional setup after loading the view, typically from a nib.
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
然后,添加几个个辅助方法,减少我们的代码量
创建默认的描述字典
func createDefaultKeyChainItemDic()->NSMutableDictionary{
var keyChainItem = NSMutableDictionary()
keyChainItem.setObject(kSecClassInternetPassword as NSString, forKey: kSecClass as NSString)
keyChainItem.setObject("blog.csdn.net/hello_hwc", forKey: kSecAttrServer as NSString)
keyChainItem.setObject(self.usernameTextfield.text, forKey: kSecAttrAccount as NSString)
return keyChainItem
}
用UIAlertController提示信息
func alertWithMessage(message:String){
var alertController = UIAlertController(title:"Info", message: message, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title:"OK", style: UIAlertActionStyle.Cancel, handler:nil))
self.presentViewController(alertController, animated: true, completion: nil)
}
func alertWithStatus(status:OSStatus){
if(status == 0){
self.alertWithMessage("Success")
}else{
self.alertWithMessage("Fail ErrorCode is\(status)")
}
}
添加KeyChain
- 这里用函数SecItemCopyMatching来查找这个keyChain是否存在。两个参数,第一个是描述字典,第二个是查找结果拷贝到的字典,通常只有在获取的时候才会用到,这里为nil即可。
- kSecValueData这个key是实际要保存的密码,要先转换成NSData
- SecItemAdd这个函数来添加keyChain,返回值是OSStatus类型,错误类型较多,可以Google。这里知道0是没有错误就可以了。
@IBAction func addKeyChainItem(sender: AnyObject) {
var keyChainItem = self.createDefaultKeyChainItemDic()
if SecItemCopyMatching(keyChainItem,nil) == noErr{
self.alertWithMessage("User name already exits")
}else{
keyChainItem.setObject(self.passwordTextField.text.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion:true)!, forKey: kSecValueData as String)
var status = SecItemAdd(keyChainItem, nil)
self.alertWithStatus(status)
}
}
更新KeyChain
SecItemUpdate函数用来更新KeyChain,两个参数,第一个参数是描述字典,第二个是包含更新数据的字典
@IBAction func updateKeyChainItem(sender: AnyObject) {
var keyChainItem = self.createDefaultKeyChainItemDic()
if SecItemCopyMatching(keyChainItem,nil) == noErr{
var updateDictionary = NSMutableDictionary() updateDictionary.setObject(self.passwordTextField.text.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion:true)!, forKey:kSecValueData as String)
var status = SecItemUpdate(keyChainItem,updateDictionary)
self.alertWithStatus(status)
}else{
self.alertWithMessage("The keychain doesnot exist")
}
}
删除keyChain
SecItemDelete函数用来删除
@IBAction func deleteKeyChainItem(sender: AnyObject) {
var keyChainItem = self.createDefaultKeyChainItemDic()
if SecItemCopyMatching(keyChainItem,nil) == noErr{
let status = SecItemDelete(keyChainItem)
self.alertWithStatus(status)
}else{
self.alertWithMessage("The keychain doesnot exist")
}
}
获取KeyChain
SecItemCopyMatching第二个参数包含了获取到的字典信息。转换方式有点复杂。
@IBAction func getKeyChainItem(sender: AnyObject) {
var keyChainItem = self.createDefaultKeyChainItemDic()
keyChainItem.setObject(kCFBooleanTrue, forKey: kSecReturnData as String)
keyChainItem.setObject(kCFBooleanTrue, forKey: kSecReturnAttributes as String)
var queryResult: Unmanaged<AnyObject>?
let status = SecItemCopyMatching(keyChainItem,&queryResult)
let opaque = queryResult?.toOpaque()
var contentsOfKeychain: NSString?
if let op = opaque {
let retrievedData = Unmanaged<NSDictionary>.fromOpaque(op).takeUnretainedValue()
let passwordData = retrievedData.objectForKey(kSecValueData) as! NSData
let passwordString = NSString(data: passwordData, encoding: NSUTF8StringEncoding)!
self.alertWithMessage("Password: \(passwordString)")
}else{
self.alertWithMessage("The keychain doesnot exist")
}
}
总结
简单来讲,一共就是四个函数
- SecItemCopyMatching - 查询和获取
- SecItemUpdate - 更新
- SecItemAdd - 添加
- SecItemDelete - 删除
欢迎关注我的iOS详解专栏,这里我会讲解绝大部分iOS常用的技术
http://blog.csdn.net/column/details/huangwenchen-ios-sdk.html
我的博客iOS部分目录
http://blog.csdn.net/hello_hwc/article/details/45365385