iOS/Common

키체인(Keychain)에 대한 설명과 간단한 사용법

TDCIAN 2022. 4. 20. 14:28

iOS 앱 개발 과정에서 로컬(모바일 기기)에 특정 정보를 저장할 때

간단하게는 유저디폴트(UserDefaults)부터 코어데이터, Realm, SQLite 등 여러 저장 방식을 사용할 수 있습니다.

하지만 정말 중요한 사용자 정보들(예를 들어 패스워드, 프라이빗 키, 인증서 등)을

아무 곳에나 저장하는 것은 아주 위험하다고 할 수 있겠습니다!

 

민감한 정보를 안전하게 저장하기 위해 애플은 Keychain Services를 제공합니다.

 

키체인이라 불리는 암호화된 데이터베이스에 사용자의 데이터를 저장하면 아주 좋거든요

 

 

 

여러분들은 사용자의 민감 정보를 암호화 해서 키체인에 저장하고, 이후에 이를 다시 복호화 해서 사용할 수 있습니다!

 

결국 중요한 것은 사용 방법이겠지요?

 

키체인을 생성(Create)하고, 읽고(Read), 지우는(Delete) 방법을 한 번 알아봅시다!

 

바로 전체 코드를 보여드리겠습니다!

 

 

import UIKit
class ViewController: UIViewController {
let account = UIDevice.current.identifierForVendor?.uuidString ?? "" // 기기의 UUID
let service = Bundle.main.bundleIdentifier ?? "" // 앱의 번들아이덴티파이어
override func viewDidLoad() {
super.viewDidLoad()
print("viewDidLoad - 기존에 있던 패스워드 지우기")
print("어카운트: \(account), 서비스: \(service)")
deletePassword()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("viewDidLoad - 새로 패스워드를 저장하기")
savePassword()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("viewWillAppear - 새로 저장한 패스워드 불러오기")
getPassword()
}
func savePassword() {
do {
try KeychainManager.save(
service: service,
account: account,
password: "내가 저장하고 싶은 패스워드".data(using: .utf8) ?? Data() // 여기에 여러분이 저장하고자 하는 중요 정보를 넣으시면 됩니다
)
} catch {
print(error)
}
}
func getPassword() {
guard let data = KeychainManager.get(
service: service,
account: account
) else {
print("getPassword() - 불러올 수 있는 패스워드가 없습니다")
return
}
let password = String(decoding: data, as: UTF8.self)
print("getPassword() - 불러온 패스워드: \(password)")
}
func deletePassword() {
guard let data = KeychainManager.get(
service: service,
account: account
) else {
print("deletePassword() - 불러올 수 있는 패스워드가 없습니다")
return
}
KeychainManager.delete(service: service, account: account, password: data)
}
}
class KeychainManager {
enum KeychainError: Error {
case duplicateEntry
case unknown(OSStatus)
}
static func save(service: String, account: String, password: Data) throws {
let query: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword, // 키체인 아이템 클래스 타입
kSecAttrService as String: service as AnyObject, // 서비스 아이디 -> 앱 번들 아이디
kSecAttrAccount as String: account as AnyObject, // 저장할 아이템의 계정 이름
kSecValueData as String: password as AnyObject // 저장할 아이템의 데이터
]
let status = SecItemAdd(query as CFDictionary, nil) // 키체인에 하나 이상의 항목을 추가할 때 사용
guard status != errSecDuplicateItem else {
throw KeychainError.duplicateEntry
}
guard status == errSecSuccess else {
throw KeychainError.unknown(status)
}
print("save() - status: \(status)")
}
static func get(service: String, account: String) -> Data? {
let query: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service as AnyObject,
kSecAttrAccount as String: account as AnyObject,
kSecReturnData as String: kCFBooleanTrue,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
// 검색 쿼리와 일치하는 키체인 항목을 하나 이상 반환하는 기능, 특정 키 체인 항목의 속성을 복사할 수 있음
let status = SecItemCopyMatching(
query as CFDictionary,
&result
)
print("get() - status: \(status)")
return result as? Data
}
static func delete(service: String, account: String, password: Data) {
let query: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword, // 키체인 아이템 클래스 타입
kSecAttrService as String: service as AnyObject, // 서비스 아이디 -> 앱 번들 아이디
kSecAttrAccount as String: account as AnyObject, // 저장할 아이템의 계정 이름
kSecValueData as String: password as AnyObject // 저장할 아이템의 데이터
]
let passwordToDelete = String(decoding: password, as: UTF8.self)
print("delete() - 삭제할 패스워드 - \(passwordToDelete)")
let status = SecItemDelete(query as CFDictionary)
print("delete() - status: \(status)")
}
}

 

 

전체 GitHub Code는 여기에서 다운로드 받으시면 됩니다!

https://github.com/TDCIAN/KeychainExample

 

GitHub - TDCIAN/KeychainExample: based on: https://www.youtube.com/watch?v=cQjgBIJtMbw&list=RDCMUCnksRRifsSCGUZpQukUKAyg&start_r

based on: https://www.youtube.com/watch?v=cQjgBIJtMbw&list=RDCMUCnksRRifsSCGUZpQukUKAyg&start_radio=1&rv=cQjgBIJtMbw&t=1053 - GitHub - TDCIAN/KeychainExample: based on: https://www....

github.com

 

위 코드는 참고 자료에 있는 여러 내용들을 종합해서 제가 별도로 만든 내용입니다.

가장 기초적인 영상 설명을 원하신다면 이 링크를 클릭해주세요!

https://www.youtube.com/watch?v=cQjgBIJtMbw%20 

 

키체인의 특성상 유저디폴트와 다르게 여러분이 앱을 한 번 삭제 하더라도

저장된 내용(정확하게는 키체인에서 kSecAttrService와 kSecAttrAccount로 조회했을 때 일치하는 내용)은 지워지지 않습니다!

 

 

 

* 혹시라도 잘못된 부분을 발견하셨거나, 조언하고 싶으신 내용이 있으시다면 댓글로 남겨주세요!

 

 

 

참고 자료

- https://www.youtube.com/watch?v=cQjgBIJtMbw 

- https://developer.apple.com/documentation/security/keychain_services

- https://adora-y.tistory.com/entry/iOS-KeyChain%EC%9D%B4%EB%9E%80-Swift%EC%BD%94%EB%93%9C%EB%A5%BC-%ED%86%B5%ED%95%B4-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0