KeyChain에 대해
- KeyChain
- Keychain
Keychain Service에 대해
Keychain services | Apple Developer Documentation

KeyChain은 디바이스 내부에 위치한 암호회된 데이터베이스 입니다.
애플리케이션 내부에 데이터를 직렬화하여 저장하는 UserDefaults와 달리 KeyChain은 디바이스 내부에 데이터를 저장하므로 앱을 삭제해도 남아있습니다.
이러한 특성 때문에 인증 정보 등 지속적으로 관리를 해야하는 민감한 데이터들을 저장하기에 용이합니다.
KeyChain은 일반적인 저장 방식과 달리 위 첨부된 사진과 같이 비밀번호에 국한되지 않습니다.
내부적으로 OS에서 직접 관리하며 하드웨어 레벨 암호화를 지원하기 때문에 다양한 정보를 저장할 수 있습니다.
KeyChain에 저장하는 대표적인 민감한 정보들을 예시로 들어오면 다음과 같습니다.
- 로그인 비밀번호
- 인증 토큰
- 결제 데이터
- 신용카드 정보
- 알고리즘 암호키
- 인증서
KeyChain API
KeyChain을 이용하기 위한 API는 Security라는 퍼스트 파티에 정의되어 있습니다.


1. Keychain Item

공식문서에서는 KeyChain에 특정 기밀 정보(Data)를 저장할 때 이를 암호화(Encryption)시켜 Item의 형태로 저장한다고 합니다.
또한 Data와 Attribute를 함께 포장해서 1개의 Item으로 만듭니다.
이 Attribute를 통해 Keychain에 저장된 Data를 검색하여 접근할 수 있습니다.
Keychain = Data(Encryption) + Attributes)
이를 통해 Keychain Service는 Data를 암호화하고 Attribute와 함께 KeyChain에 Item을 저장하는 것을 관리해주는 역할을 수행함을 알 수 있습니다.
추후 승인된 프로세스 혹은 접근 권한이 부여된 프로세스(authorized processes) 를 통해 KeyChain Service를 사용하여 Attribute를 통해 Item을 찾고 Data의 암호를 해제할 수 있습니다.
As shown in Figure 1, keychain services handles data encryption and storage (including data attributes) in a keychain, which is an encrypted database stored on disk. Later, authorized processes use keychain services to find the item and decrypt its data.
2. Keychains
Keychains | Apple Developer Documentation
위 문서를 보면 iOS와 MacOS 환경에서의 KeyChain이 다르다는 것을 알 수 있습니다.
🤔
하지만 문서 하단에 있는 Topics에 명시된 API 대다수가Deprecated되어있어.. iOS와 MacOS의 KeyChain 관리 방식을 통일시키려고 하는 것처럼 느껴졌습니다. 그래도 중요한 API는 남아있네요..
- iOS의 KeyChain
- iOS의 여러 앱들은
single Keychain에 접근할 수 있습니다. - 이때 single Keychain은 앱마다 개별적인 KeyChain을 가지는 것이 아닌, N개의 앱들이 같은 KeyChain을 공유한다..라는 의미인 것 같습니다.
- 아이폰의 잠금(Lock), 잠금 해제(UnLock) 상태에 따라 KeyChain도 자동으로 동일하게 잠금/잠금 해제 상태가 됩니다.
- ⭐ 여러 앱이 동일한
KeyChain을 공유하고 있지만특정한 1개의 앱이 KeyChain에 들어있는 모든 Item들에 접근할 수 있는 것은 아닙니다.
→ 즉, 특정 앱은 자신이 가진 KeyChain Item에만 접근할 수 있으며 만약 소속된 Group이 있는 경우 해당 Group의 Item에는 접근할 수 있다고 적혀있네요… 👀
- macOS의 KeyChain
- macOS는 여러 개의 KeyChain을 가질 수 있습니다.
- 특히 맥북에 있는 Keychain Access 앱을 통해
사용자가 직접 KeyChain을 관리할 수 있고 iOS와 마찬가지로 기본 키체인으로 작업할 수 있습니다.

3. Access Control Lists
Access Control Lists | Apple Developer Documentation
위 내용은 macOS에서 KeyChain Item에 접근 가능한 앱을 제어하는 방법에 대해 서술되어 있습니다.
하지만 이는 iOS와 iCloud 기반 KeyChain을 사용하는 macOS에선 사용할 수 없다고 적혀있어 그냥 넘어갔습니다. 👀
4. KeyChain을 사용한 기밀 정보 관리법
Using the keychain to manage user secrets | Apple Developer Documentation
위 문서는 사용자의 경험을 중심으로 앱이 최소한의 개입으로 안전하게 사용자의 민감한 정보를 관리하는 방법에 대해 서술하고 있었습니다.

위 이미지는 공식문서에서 예시를 둔 인터넷 비밀번호 저장 프로세스 입니다.
위 흐름을 토대로 주요 API에 대해 설명해보겠습니다.
KeyChain에 저장되는 민감한 정보(Data)를 사용자의
비밀번호라고 가정해보겠습니다.
4-1. 처음 로그인 시도 시 SecItemAdd(_:_)

- 앱 실행 후 KeyChain에 자격 증명이 없을 경우 사용자에게 직접 입력을 요청합니다. (prompt user)
- 사용자가 입력한 후 인증에 성공했을 시
SecItemAdd(_:_)을 통해 KeyChain에 값을 저장합니다. - 이후 재로그인없이 Keychain에서 비밀번호를 자동으로 조회시켜 로그인을 시도합니다.
4-2. 일반적인 재인증 시 SecItemCopyMatching(_:_:)

- 앱을 재실행하거나 세션 만료 등의 상황이 발생했을 때 활용됩니다.
- 앱이
SecItemCopyMatching를 사용하여 Keychain을 검색합니다. - 만약 KeyChain 내부에 사용자의 비밀번호가 존재한다면 바로 인증을 진행합니다.
- 이는 사용자 개입 없이 자연스럽게 로그인이 유지될 수 있도록 해줍니다.
4-3. 비밀번호가 변경되거나 갱신되었을 때 SecItemUpdate(_:_:)

- KeyChain은 외부에서 수정될 수 있습니다.
- 사용자가 웹이나 외부 환경에서 비밀번호를 변경했다면 KeyChain에 저장되어있는 Data는 자연스럽게 레거시가 되어 인증이 실패하는 상황이 발생합니다.
- 만약 인증이 실패하는 상황이 발생한다면 앱은
사용자 입력을 다시 요청합니다.
→ 이때 SecItemUpdate(_:_:) 을 통하여 기존 Keychain 값을 갱신합니다.
4-4. 로그아웃 또는 연결 해제 시 SecItemDelete(_:)

- 사용자가 특정 계정을 완전히 삭제하거나 민감한 정보를 제거하고 싶을 경우 Keychain에 존재하는 데이터를 제거해야합니다.
- 이를 위해
SecItemDelete(_:)을 호출하여 Keychain 내부의 자격 증명을 완전히 삭제할 수 있습니다.
위를 통하여 사용자가 직접적으로 개입해야하는 부분에 대해 표로 정리하면 아래와 같습니다.
| 상황 | 사용자 개입 | 주요 함수 | 동작 |
|---|---|---|---|
| 최초 로그인 | 필요 | SecItemAdd | 새로운 자격 증명 저장 |
| 자동 로그인 | 불필요 | SecItemCopyMatching | 기존 정보 조회 |
| 비밀번호 변경 | 필요 | SecItemUpdate | 기존 정보 갱신 |
| 로그아웃 | 불필요 | SecItemDelete | 정보 삭제 |
직접 만들어보기
Adding a password to the keychain | Apple Developer Documentation
이제 위 공식문서를 토대로 직접 keychain을 사용해보겠습니다.
우선 앱에서 사용할 자격 증명 정보를 저장할 구조체를 정의합니다.
struct Credentials {
var username: String
var password: String
}
그 후 Keychain 접근 결과를 나타내기 위한 Error 열거형을 정의합니다.
enum KeychainError: Error {
case noPassword
case unexpectedPasswordData
case unhandledError(status: OSStatus)
}
이때 대부분의 API 반환값이 _
OSStatus_로 되어있는데.. 아무리 찾아봐도 관련된 내용을 찾을 수 없었습니다..ㅠㅠ
https://developer.apple.com/documentation/security/security-framework-result-codes
이런 링크가 있긴 했지만 이해가 잘 안가네요.. 검색 결과론 _macOS 및 iOS와 같은 Apple 운영체제 레벨에서 특정 함수의 결과를 나타내는 정수 코드_라고 나오는데.. 잘 모르겠습니다.
KeyChain 함수를 호출 후 반환된OSStatus값을 확인하여 _noErr(성공, 값은 0)__이라면 그냥 다음 단계로, 특정 정수가 출력되었다면 어떠한 오류가 발생했다는 걸로 인지했습니다.이와 관련된 OSStatus의 값을 찾을 수 있는 사이트는 아래와 같습니다._
https://www.osstatus.com/
마지막으로 앱의 서버를 입력합니다. (재인증을 시도할 서버를 말하는 것 같습니다.)
let server = "www.example.com"
이제 정의한 내용을 토대로 KeyChain에 저장하기 위핸 쿼리를 작성해보면 다음과 같습니다.
let credentials = Credentials(username: "daehyeon", password: "1234")
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]
이때 kSecClass as String: kSecClassInternetPassword 가 존재하는데 kSecClass의 정의는 아래와 같습니다.

KSecClass 는 KeyChain Services에서 사용되는 딕셔너리의 키를 의미합니다.
해당 키는 KeyChain Item의 클래스를 나타내며 어떤 종류의 Item을 생성할 지, 검색할 지 지정하는 데 사용됩니다.
KeyChain은 단순 비밀번호가 아닌 신용카드 번호, 인증서 등 다양한 데이터의 저장을 지원하는데 이 kSecClass를 통해 어떤 유형의 데이터를 저장할 지, 검색할 지를 결정할 수 있습니다.
이 Key에 따라 저장할 수 있는 데이터의 종류도 달라집니다.
해당 정리는 아래 블로그에 정리가 매우 잘 되어있어서 링크로 남기겠습니다.
kSecClass & Item Class Value in Keychain - 키체인 — IT Document
kSecAttrAccount as String: accountkSecAttrServer as String: server
→ 이 2가지 데이터는 Attribute 정보를 의미합니다.
kSecValueData as String: password- 위 데이터가 가장 중요하며 실제 저장될 민감한 정보를 의미합니다.
- 이때
Data로 인코딩하여 query에 넣어야하기 때문에 공식 문서에서credentials.password.data(using: String.Encoding.utf8)!를 수행한 것으로 보입니다.
그 후 작성한 쿼리를 SecItemAdd에 전달하여 KeyChain에 추가하면 끝입니다.
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }
이때 SecItemAdd의 정의는 아래와 같이 되어있는데,
func SecItemAdd(
_ attributes: CFDictionary,
_ result: UnsafeMutablePointer<CFTypeRef?>?
) -> OSStatus
반환값(OSStatus )를 확인하여 KeyChain에 저장이 성공적으로 수행되었는지 항상 확인하는 것이 좋다고 합니다.
실제 저장한 KeyChain을 검색해서 가져오거나 변경되었을 때, 수정할 때, 삭제할 때 등은 추후에 더 정리하겠습니다.