Keychain - iOS

godo·2022년 10월 30일
0

Keychain

유저가 특별히 아끼는, 신용 카드 정보나 짧은 노트와 같은 정보를 저장할 수 있습니다.

물론 유저가 특별히 신경쓰지 않는 정보도 저장할 수 있습니다.

Keychain Items

위의 그림과 같이
데이터와 같이, 볼 수 있는 속성들을 같이 주어 검색이 가능하도록 합니다.
나중에, 권한을 얻는 과정은 Keychain services 를 이용해서 아이템을 찾고 decrypt 합니다.

사용 방법

App 들은 종종 패스워드와 같은 민감한 유저 데이터들에 접근이 필요합니다, 하지만 데이터를 계속해서 secure 하게 하는 것은 대가가 따릅니다.

만약 데이터를 암호화하지 않고 저장하게 된다면, 보안의 위험을 만들게 됩니다.

대신 계속 유저에게 요청하게 된다면, 유저가 단순한 패스워드를 사용하거나 패스워드를 써 놓는 나쁜 유저 경험을 유발하게 됩니다.

Keychain 서비스는 이 문제를 간단한 접근과 보안화를 통해서 해결할 수 있도록 했습니다.

너의 앱은 keychain 을 이용해서, 최소한의 유저 소통으로, 좋은 유저 경험을 제공합니다.

[SecItemAdd(_:_:)]을 통해서 저장을 합니다.

이제 keychain 은 저장된 값을 바탕으로 재인증 요청이 있을 때 유저를 괴롭히지 않습니다.

준비

에러

enum KeychainError: Error {
        case duplicateError
        case unKnown(OSStatus)
    }

정보

struct Credentials {
    var username: String
    var password: String
}

쿼리 만들기

let account = credentials.username
let password = credentials.password.data(using: String.Encoding.utf8)!
var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
                            kSecAttrAccount as String: account,
                            kSecAttrServer as String: server,
                            kSecValueData as String: password]

아이템 추가

let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }

저장된 값 가져오기

쿼리 준비

let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
                            kSecAttrServer as String: server,
                            kSecMatchLimit as String: kSecMatchLimitOne,
                            kSecReturnAttributes as String: true,
                            kSecReturnData as String: true]

검색 시작

var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status != errSecItemNotFound else { throw KeychainError.noPassword }
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }

정보 얻기

guard let existingItem = item as? [String : Any],
    let passwordData = existingItem[kSecValueData as String] as? Data,
    let password = String(data: passwordData, encoding: String.Encoding.utf8),
    let account = existingItem[kSecAttrAccount as String] as? String
else {
    throw KeychainError.unexpectedPasswordData
}
let credentials = Credentials(username: account, password: password)

Handle Change Gracefully

쿼리 준비

let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
                            kSecAttrServer as String: server]

새로운 정보 준비

let account = credentials.username
let password = credentials.password.data(using: String.Encoding.utf8)!
let attributes: [String: Any] = [kSecAttrAccount as String: account,
                                 kSecValueData as String: password]

업데이트 실행

let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
guard status != errSecItemNotFound else { throw KeychainError.noPassword }
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }

더 이상 필요 없는 데이터 삭제

let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess || status == errSecItemNotFound else { throw KeychainError.unhandledError(status: status) }

출처

https://developer.apple.com/documentation/security/keychain_services
https://developer.apple.com/documentation/security/keychain_services/keychain_items
https://developer.apple.com/documentation/security/keychain_services/keychain_items/using_the_keychain_to_manage_user_secrets
https://developer.apple.com/documentation/security/keychain_services/keychain_items/adding_a_password_to_the_keychain
https://developer.apple.com/documentation/security/keychain_services/keychain_items/searching_for_keychain_items
https://developer.apple.com/documentation/security/keychain_services/keychain_items/updating_and_deleting_keychain_items

git : https://github.com/godo129/Keychain_Ex

profile
☀️☀️☀️

0개의 댓글