일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- roundingMode
- Modality
- viewcontroller
- delegation
- NumberFormatter
- 아이폰
- Info.plist
- UIResponder
- 스위프트
- SWIFTUI
- @available
- contentInset
- Navigation
- Mock
- Failed to register bundle identifier
- 책후기
- Structures and Classes
- 스타트업주니어로살아남기
- IOS
- xcode
- 독후감
- SWIFT
- mvvm
- 독서후기
- human interface guidelines
- 부트캠프
- Codegen
- NotificationCenter
- View Life Cycle
- 야곰아카데미
- Today
- Total
호댕의 iOS 개발
[iOS] Keychain 본문
야곰 아카데미 불타는 토요 스터디에서 진행한 Keychain 관련 공부입니다.
🔐 Keychain은 뭘까?
참고문서: https://developer.apple.com/documentation/security/keychain_services
일단 Keychain은 사용자 대신 작은 단위의 데이터를 안전하게 저장하는 공간을 의미한다. 여기서 작은 단위의 데이터는 인증서, 비밀번호, 인적사항 등의 데이터를 말한다.
이때 keychain service API는 키체인이라는 암호화된 데이터베이스에 사용자의 데이터를 저장하는 매커니즘을 가지고 있다.
단 iOS에서는 하나의 디바이스에 하나의 keychain만 존재하며 번들 단위(앱 단위)로 keychain을 관리하게 되며, MacOS에선 임의로 키체인을 생성할 수 있다는 차이가 존재한다.
📑 Keychain Item
참고문서: https://developer.apple.com/documentation/security/keychain_services/keychain_items
키체인에 저장하게 되는 암호화한 정보를 의
미한다. 예를 들어 패스워드 같은 것을 저장하기 위해선 keychain item으로 만들어줘야 한다.
🔖 Keychain Attribute
데이터를 저장하기 위해선 keychain item은 attribute가 필요하다. attribute는 나중에 item을 찾을 수 있도록 도와주며, 데이터를 사용하거나 공유하는 방법을 관리할 수 있도록 한다. 즉, 데이터를 찾기 위한 tag 역할인 것이다.
그렇다면 본격적으로 keychain을 어떻게 사용하면 되는지 알아보자.
1️⃣ Adding a Password to the Keychain
일단 가장 먼저 Keychain에 keychain item을 넣는 방법에 대해 알아보자.
이번에는 비밀번호만 keychain에 넣을 예정이라 password 프로퍼티만 생성해주었다.
struct Credentials {
var password: String
}
textfield를 통해 받은 값을 비밀번호에 넣어줬고, 이를 keychain에 넣어주기 위해 기존에 string이었던 비밀번호를 data로 변경해주었다.
guard let inputPassword = passwordTextField.text else { return }
let credential = Keychain.Credentials(password: inputPassword)
guard let password = credential.password.data(using: .utf8) else { return }
그 후 암호화된 정보를 어디에 넣을지 정해줘야 하기 때문에 query를 정해줬다.
var query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword, // 암호화된 정보를 어디에 넣을 지를 정함(어떤 질문인지 정함)
kSecValueData as String: password // 내가 여기 넣을 데이터는 password야.
]
이후 SecItemAdd 메서드를 사용해서 keychain에 값을 넣어줬다. 위 함수는 OSStatus 값을 반환해주어 키체인에 넣었을 때 상태가 어떤지를 알려주게 된다.
OSStatus의 경우 숫자로 나오게 되며 OSStatus라는 사이트를 통해 어떤 상태인지를 파악할 수 있다. 현재는 Security 프레임워크를 기반으로 하는 Keychain에 대해 진행하고 있기 때문에 위 사이트에서 Security 프레임워크의 상태를 확인해보면 된다.
2️⃣ Searching for Keychain Items
이렇게 keychain에 추가를 했으면 이를 다시 찾을 수도 있어야 한다. 그래야 비밀번호도 비교해보고 할 수 있으니 말이다.
이때도 query를 따로 만들어줘야 한다. add에서 필요한 질문(query)과 찾을 때의 query가 다를 수 있기 때문이다.
var query: [String: Any] = [ // query 질의 -> 물어보는 것. 듣는 쪽은 키체인이고 말하는 것은 우리
kSecClass as String: kSecClassGenericPassword,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnAttributes as String: true,
kSecReturnData as String: true
]
일단 Data를 반환받아야 하기 때문에 kSecReturnData를 true로 해주었고 맞는 비밀번호 하나만 필요하기 때문에 kSecMatchLimit을 kSecMatchLimitOne로 설정해주었다. 이때 kSecReturnAttributes를 true로 해주는 코드가 없을 경우 이를 통해 제대로 값을 찾지 못했다.
attribute는 따로 넣어주지 않고 데이터만 넣어줬는데도 true로 해줘야 했다. 이에 대해선 아직 정확한 이유를 파악하진 못했으나 attribute를 Return해줘야 딕셔너리의 키 값이 생기는 것이 아닌가 추측해보았다.
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status != errSecItemNotFound else {
return
showAlert(message: "비밀번호 생성해줘")
}
guard let existingItem = item as? [String:Any],
let passwordData = existingItem[kSecValueData as String] as? Data,
let originPassword = String(data: passwordData, encoding: .utf8),
inputPassword == originPassword else {
showAlert(message: "다른 사람의 일기장을 보면 안돼 🖐🏻")
return
}
그리고 item 변수를 생성해주었다. 이때 item의 Reference를 전달해줘서 CFTypeRef를 사용해줬다.
따라서 SecItemCopyMatching의 이름이 그냥 Matching이 아니라 CopyMatching인 이유도 reference인 만큼 Heap에 있는 값을 변경하면 안되기 때문에 copy를 하는 것이 아닐까 생각해봤다. 그리고 매개변수로 받은 item을 변경해주기 떄문에 &을 붙여줬다.
3️⃣ Updating and Deleting Keychain Items
마지막으로 keychain을 업데이트하거나 삭제하는 방법이다.
이것도 앞과 거의 비슷하며 SecItemDelete와 SecItemUpdate를 사용하면 된다.
이때 쿼리는 다음과 같이 작성해주었다.
var query: [String: Any] = [ // 기존에 등록되었을 때 사용한 쿼리
kSecClass as String: kSecClassGenericPassword,
kSecValueData as String: password
]
var updateQuery: [String: Any] = [
kSecValueData as String: password // 바꿔야 하는 항목만 기재
]
그리고 위처럼 이렇게 해주면 끝난다.
let editstatus = SecItemUpdate(query as CFDictionary, updateQuery as CFDictionary)
삭제의 경우 더욱 간단하다. 단순히 기존의 query 그대로 가져와 지워주기만 하면 된다.
let status = SecItemDelete(query as CFDictionary)
'Software Engineering > iOS' 카테고리의 다른 글
[iOS] 키보드가 컨텐츠를 가리지 않게 하는 법 (0) | 2022.02.12 |
---|---|
[iOS] TextField의 테두리에 색깔 넣기(조건이 안 맞는 경우만!) (0) | 2022.01.24 |
[iOS] 이미지의 크기 자체를 줄이고 싶다면? (0) | 2022.01.22 |
[iOS] 이미지를 받고, 이미지의 크기를 크롭하기 (0) | 2022.01.22 |
[iOS] FileManager를 통해 파일을 생성해보자. (0) | 2022.01.10 |