iOS中使用鑰匙串

文件保護用來保護**數據**,而鑰匙串則用來保護**祕密**。在這裏,祕密是指用來訪問其他數據的一小段數據。最常見的祕密就是密碼和私鑰了。

鑰匙串由操作系統保護,在設備鎖定時會進行加密處理。實際上,它的工作原理跟文件保護很像。不幸的是,Keychain API並不友好,所以許多開發人員爲Keychain API做了一些包裝。不過,筆者推薦使用的是蘋果GenericKeychain示例代碼中的KeyChainItemWrapper。本節將在簡要介紹底層的數據結構之後介紹它。


KeyChainItemWrapper有一些問題,其中最突出的問題就是它並不兼容ARC。可以將KeyChainItemWrapper.m文件的ARC關掉,或者增加需要的__bridge類型轉換。在網絡上搜索GenericKeyChain ARC,可以找到一些做過這方面工作的人寫的文章。它並不像我們想的那樣方便。有若干種方法都能用來完成這個任務。雖然這些方法都還不成熟,尚不足在這裏推薦,但一切都在改進。關注一下iosptl.com上的更新。


鑰匙串中的條目稱爲SecItem,但它是存儲在CFDictionary中的。SecItemRef類型並不存在。SecItem有五類:通用密碼、互聯網密碼、證書、密鑰和身份。在大多數情況下,我們用到的都是通用密碼。許多問題都是開發人員嘗試用互聯網密碼造成的。互聯網密碼要複雜得多,而且相比之下優勢寥寥無幾,除非開發Web瀏覽器,否則沒必要用它。KeyChainItemWrapper只使用通用密碼,這也是我喜歡它的原因之一。iOS應用很少將密鑰和身份存儲起來,所以我們在本書中不會討論這方面的內容。只有公鑰的證書通常應該存儲在文件中,而不是鑰匙串中。

最後,我們需要在鑰匙串中搜索需要的內容。密鑰有很多個部分可用來搜索,但最好的辦法是將自己的標識符賦給它,然後搜索。通用密碼條目都包含屬性kSecAttrGeneric,可以用它來存儲標識符。這也是KeyChainItemWrapper的處理方式。

鑰匙串中的條目都有幾個可搜索的**屬性**和一個加密過的**值**。對於通用密碼條目,比較重要的屬性有賬戶(kSecAttrAccount)、服務(kSecAttrService)和標識符(kSecAttrGeneric)。而值通常是密碼。

有了這個背景,現在我們可以看看如何使用KeyChainItemWrapper了。首先,如下代碼所示,可以用initWithIdentifier:accessGroup:來創建一個密碼。稍後介紹訪問組的概念,先將它保留爲nil

1
2
3
4
KeychainItemWrapper *
  wrapper = [[KeychainItemWrapper alloc]
                               initWithIdentifier:@"MyKeychainItem"
                                      accessGroup:nil];

現在可以對wrapper進行讀取和寫入了,就跟使用NSDictionary一樣。它會自動跟鑰匙串同步。__bridge強制轉換用來將Core Foundation的常量傳給一個使用ARC的Cocoa方法。

1
2
3
4
  id kUsernameKey = (__bridge id)kSecAttrAccount;
  id kPasswordKey = (__bridge id)kSecValueData;
  NSString *username = [wrapper objectForKey:kUsernameKey];
  [wrapper setObject:password forKey:kPasswordKey];

注意,我使用的是個Core Foundation類型(kSecAttrAccount),這裏它需要用到idobjectForKey:),不要用類型轉換,也不要用__bridge。這是一個新特性。


KeyChainItemWrapper會緩存讀入數據,但不會緩存寫出的數據。向鑰匙串中寫數據的成本很高,所以我們不會頻繁寫入。鑰匙串不適合用來存儲經常改變的敏感數據。那類數據應該保存到加密文件中,可以參考15.3節的內容。

發佈了76 篇原創文章 · 獲贊 13 · 訪問量 65萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章