iOS Keychain總結(jié)

概要

iOS中一般用Keychain存儲(chǔ)密碼、私鑰等需要加密的數(shù)據(jù)。參考apple 官網(wǎng)介紹

使用方法

先總的說下怎么使用,再具體說說原理。
我自己實(shí)現(xiàn)了幾個(gè)基本的功能:查找、更新、刪除、添加。后續(xù)會(huì)繼續(xù)補(bǔ)充更多的功能。
已開源到github

  1. 在工程中添加Secutity.framework庫。(TARGETS-General-Linked Frameworks and Libraries)
  2. 使用說明
#import "NYKeychain.h"http://引入頭文件
NSString *serviceName = xxx;//服務(wù)名
NSString *account = xxx;//賬戶名
//其中serviceName和account在程序內(nèi)部自己定義的常量,keychain根據(jù)這兩個(gè)量保存、查找、更新、替換密碼
[NYKeychain setPassword:password forService:serviceName account:account];//保存、更新密碼
[NYKeychain passwordForService:serviceName account:account];//查找
 [NYKeychain deletePasswordForService:serviceName account:account];//刪除

原理

keychain相當(dāng)于一個(gè)銀行的金庫,keychain item就相當(dāng)于金庫里的一個(gè)個(gè)的保險(xiǎn)柜,保存在keychain Item里面的密碼就相當(dāng)于保險(xiǎn)柜里面存放的金條。

keychain結(jié)構(gòu)
  • 銀行金庫里有多個(gè)保險(xiǎn)柜,keychain有多個(gè)keychain item;每個(gè)保險(xiǎn)柜都有兩部分組成:保險(xiǎn)柜里面的金條以及保險(xiǎn)柜的屬性(保險(xiǎn)柜編號(hào)、保險(xiǎn)柜位置……);每一個(gè)keychain item包含兩部分:數(shù)據(jù),一系列屬性。
  • 保險(xiǎn)柜的屬性與其要存放的東西有關(guān):可能存放黃金的保險(xiǎn)柜在A區(qū),保險(xiǎn)柜編號(hào)以G開頭;而存放重要資料的保險(xiǎn)柜在B區(qū),保險(xiǎn)柜編號(hào)以D開頭……而keychain item屬性與其要保存的密碼有關(guān):是generic passwords還是Internet passwords。例如Internet passwords的屬性有security domain,protocol type等等。
  • 每個(gè)人只能打開自己的保險(xiǎn)柜,每個(gè)iOS應(yīng)用也只能使用自己的keychain items.
Keychain 服務(wù)搜索字典

iOS中Keychain Services使用key-value dictionary來指定我們想要?jiǎng)?chuàng)建或者查找的keychain item屬性。
一個(gè)典型的搜索字典包含以下四部分:

  • The class key-value pair:指定了keychain item類型。
  • 一個(gè)或多個(gè)key-value pairs:指定keychain item的各種一般屬性
  • 一個(gè)或多個(gè)key-value pairs:指定keychain item的各種查找屬性,例如kSecMatchPolicy,為了查找的精確性
  • 一個(gè)返回類型的key-value pair:指定想要的返回結(jié)果的類型,例如是返回一個(gè)dictionary或者persistent reference
    具體可以指定什么屬性,可以查找apple官方定義
    例如想要執(zhí)行一個(gè)有如下屬性的搜索:想要Apple Store(service name)下ImaUser(account)賬戶下密碼,該搜索條件不敏感,可以用SecItemCopyMatching函數(shù),如圖所示:
搜索dictionary

代碼詳解

與Keychain相關(guān)的操作最重要的就是dictionary。所有操作都是圍繞配置這個(gè)dictionary。
在下面的代碼中,首先配置了這個(gè)dictionary四個(gè)部分的前兩個(gè)部分,即class key-value pair,一般屬性。

- (NSMutableDictionary *)query
{
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3];
    //要保存的password類型
    [dictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
    if (self.service) {//AttrService 和AttrAccount是兩個(gè)主要的屬性
        [dictionary setObject:self.service forKey:(__bridge id)kSecAttrService];
    }
    if (self.account) {
        [dictionary setObject:self.account forKey:(__bridge id)kSecAttrAccount];
    }
    return dictionary;
}
使用Keychain item 搜索dictionary

apple提供了四個(gè)函數(shù)用來進(jìn)行密碼的查找、添加、更新、刪除等操作。

SecItemAdd;
SecItemUpdate;
SecItemCopyMatching;
SecItemDelete;
  • 查找
    查找前,先配置dictionary。在下面的代碼中配置了這個(gè)dictionary四個(gè)部分的后兩個(gè)部分,即Search key-value和返回類型的key-value.
 - (BOOL)fetch
{
    CFTypeRef result = NULL;
    NSMutableDictionary *query = [self query];
    //YES本來是一個(gè)值,@YES就變成了一個(gè)對(duì)象,可用[NSNumber numberWithBool];代替
    [query setObject:@YES forKey:(__bridge id)kSecReturnData];
    [query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
    if (status != errSecSuccess) {
        return NO;
    }
    self.passwordData = (__bridge_transfer NSData *)result;
    return YES;
}
  • 添加、更新
    添加、更新前首先查找。此處的dictionary只配置了四部分中的前兩部分,原因是,apple已經(jīng)有默認(rèn)的配置。例如在kSecMatchCaseInsensitive key中有一句話:

if this attribute is not provided, then case-sensitive string matching is performed

 - (BOOL)save
{
    NSMutableDictionary *query = nil;
    NSMutableDictionary *searchQuery = [self query];
    //添加前首先查找是否存在要添加的密碼
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)searchQuery, nil);
    if (status == errSecSuccess) {//item 已經(jīng)存在,更新它
        query = [[NSMutableDictionary alloc] init];
        [query setObject:self.passwordData forKey:(__bridge id)kSecValueData];
        status = SecItemUpdate((__bridge CFDictionaryRef)searchQuery, (__bridge         CFDictionaryRef)query);
    } else if (status == errSecItemNotFound) {//item未找到,創(chuàng)建它
        query = [self query];
        [query setObject:self.passwordData forKey:(__bridge id)kSecValueData];
        status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
    }
    return (status == errSecSuccess);
}
  • 刪除
    刪除相對(duì)簡單,找到了,就刪除
 - (BOOL)deleteItem
{
NSMutableDictionary *query = [self query];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
return (status == errSecSuccess);
}

以上內(nèi)容都是基礎(chǔ),懂了這些,Keychain相關(guān)的其它內(nèi)容也就不難理解了。以后如果用到,會(huì)進(jìn)一步增加其它復(fù)雜的功能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容