OC-开发案例收集

[Swift] 钥匙串 Keychain 使用

2022-07-12  本文已影响0人  巨馍蘸酱

在 Swift 中使用钥匙串读取和保存密码

Keychain 是适用于 macOS 和 iOS 的安全存储接口,最适合用于存储小块私人数据,例如密码、cookie 和身份验证令牌。

SecItemAdd 将数据保存到钥匙串

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

SecItemAdd 用于将新项目保存到 Keychain。

第二个参数 result 是 UnsafeMutablePointer 查询指定的任何返回值。通常不需要返回数据,并且nil可以传递结果。

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 更新钥匙串中的数据

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

SecItemUpdate 用于覆盖 Keychain 中的现有 kSecValueData 数据。

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 从钥匙串中读取数据

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

就像 SecItemAdd ,SecItemCopyMatching 方法有一个 UnsafeMutablePointer 参数和一个 query 参数。读取的数据 SecItemCopyMatching 将被复制到 UnsafeMutablePointer result 供 macOS 和 iOS 应用程序访问。

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 删除钥匙串中的数据

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 钥匙串同步,则可以自动将用户的钥匙串数据与该用户的 iCloud 同步。为此,请在为所有钥匙串操作构造钥匙串服务查询时设置 kSecAttrSynchronizablekCFBooleanTrue

query[kSecAttrSynchronizable as String] = kCFBooleanTrue

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

上一篇下一篇

猜你喜欢

热点阅读