[Swift] 鑰匙串 Keychain 使用

在 Swift 中使用鑰匙串讀取和保存密碼

Keychain 是適用于 macOS 和 iOS 的安全存儲接口,最適合用于存儲小塊私人數(shù)據(jù),例如密碼、cookie 和身份驗(yàn)證令牌。

SecItemAdd 將數(shù)據(jù)保存到鑰匙串

func SecItemAdd(_ attributes: CFDictionary, _ result: UnsafeMutablePointer<CFTypeRef?>?) -> OSStatus

SecItemAdd 用于將新項(xiàng)目保存到 Keychain。

  • kSecAttrService 一個字符串,用于標(biāo)識一組鑰匙串項(xiàng)目,如 com.my-app.bundle-id
  • kSetAttrAccount 一個字符串,用于標(biāo)識特定服務(wù)中的鑰匙串項(xiàng),例如 username`@email.com
  • kSecClass 一種存儲在 Keychain Item 中的安全數(shù)據(jù),例如 kSecClassGenericPassword

第二個參數(shù) result 是 UnsafeMutablePointer 查詢指定的任何返回值。通常不需要返回數(shù)據(jù),并且nil可以傳遞結(jié)果。

static func save(password: Data, service: String, account: String) -> Bool {
    let query: [String: AnyObject] = [
        // kSecAttrService,  kSecAttrAccount, and kSecClass
        // uniquely identify the item to save in Keychain
        kSecAttrService as String: service as AnyObject,
        kSecAttrAccount as String: account as AnyObject,
        kSecClass as String: kSecClassGenericPassword,
        
        // kSecValueData is the item value to save
        kSecValueData as String: password as AnyObject
    ]
    
    // SecItemAdd attempts to add the item identified by
    // the query to keychain
    let status = SecItemAdd(query as CFDictionary, nil)

    // Any status other than errSecSuccess indicates the
    // save operation failed.
    return status == errSecSuccess
}

SecItemUpdate 更新鑰匙串中的數(shù)據(jù)

func SecItemUpdate(_ query: CFDictionary, _ attributesToUpdate: CFDictionary) -> OSStatus

SecItemUpdate 用于覆蓋 Keychain 中的現(xiàn)有 kSecValueData 數(shù)據(jù)。

static func update(password: Data, service: String, account: String) -> Bool {
    let query: [String: AnyObject] = [
        // kSecAttrService,  kSecAttrAccount, and kSecClass
        // uniquely identify the item to update in Keychain
        kSecAttrService as String: service as AnyObject,
        kSecAttrAccount as String: account as AnyObject,
        kSecClass as String: kSecClassGenericPassword
    ]
    
    // attributes is passed to SecItemUpdate with
    // kSecValueData as the updated item value
    let attributes: [String: AnyObject] = [
        kSecValueData as String: password as AnyObject
    ]
    
    // SecItemUpdate attempts to update the item identified
    // by query, overriding the previous value
    let status = SecItemUpdate(
        query as CFDictionary,
        attributes as CFDictionary
    )

    // Any status other than errSecSuccess indicates the
    // update operation failed.
    return status == errSecSuccess
}

SecItemCopyMatching 從鑰匙串中讀取數(shù)據(jù)

func SecItemCopyMatching(_ query: CFDictionary, _ result: UnsafeMutablePointer<CFTypeRef?>?) -> OSStatus

就像 SecItemAdd ,SecItemCopyMatching 方法有一個 UnsafeMutablePointer 參數(shù)和一個 query 參數(shù)。讀取的數(shù)據(jù) SecItemCopyMatching 將被復(fù)制到 UnsafeMutablePointer result 供 macOS 和 iOS 應(yīng)用程序訪問。

static func readPassword(service: String, account: String) -> Data? {
    let query: [String: AnyObject] = [
        // kSecAttrService,  kSecAttrAccount, and kSecClass
        // uniquely identify the item to read in Keychain
        kSecAttrService as String: service as AnyObject,
        kSecAttrAccount as String: account as AnyObject,
        kSecClass as String: kSecClassGenericPassword,
        
        // kSecMatchLimitOne indicates keychain should read
        // only the most recent item matching this query
        kSecMatchLimit as String: kSecMatchLimitOne,

        // kSecReturnData is set to kCFBooleanTrue in order
        // to retrieve the data for the item
        kSecReturnData as String: kCFBooleanTrue
    ]

    // SecItemCopyMatching will attempt to copy the item
    // identified by query to the reference itemCopy
    var itemCopy: AnyObject?
    let status = SecItemCopyMatching(query as CFDictionary, &itemCopy)
    
    // Any status other than errSecSuccess indicates the
    // read operation failed.
    guard status == errSecSuccess else {
        return nil
    }

    // This implementation of KeychainInterface requires all
    // items to be saved and read as Data. Otherwise, 
    // invalidItemFormat is thrown
    guard let password = itemCopy as? Data else {
        throw nil
    }

    return password
}

SecItemDelete 刪除鑰匙串中的數(shù)據(jù)

func SecItemDelete(_ query: CFDictionary) -> OSStatus

static func deletePassword(service: String, account: String) -> Bool {
    let query: [String: AnyObject] = [
        // kSecAttrService,  kSecAttrAccount, and kSecClass
        // uniquely identify the item to delete in Keychain
        kSecAttrService as String: service as AnyObject,
        kSecAttrAccount as String: account as AnyObject,
        kSecClass as String: kSecClassGenericPassword
    ]

    // SecItemDelete attempts to perform a delete operation
    // for the item identified by query. The status indicates
    // if the operation succeeded or failed.
    let status = SecItemDelete(query as CFDictionary)

    // Any status other than errSecSuccess indicates the
    // delete operation failed.
    return status == errSecSuccess
}

與 iCloud 同步鑰匙串

如果啟用了 iCloud 鑰匙串同步,則可以自動將用戶的鑰匙串?dāng)?shù)據(jù)與該用戶的 iCloud 同步。為此,請在為所有鑰匙串操作構(gòu)造鑰匙串服務(wù)查詢時設(shè)置 kSecAttrSynchronizablekCFBooleanTrue

query[kSecAttrSynchronizable as String] = kCFBooleanTrue

原文 https://www.advancedswift.com/secure-private-data-keychain-swift

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

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