-
Notifications
You must be signed in to change notification settings - Fork 0
[week14] KeyChain
Hemg edited this page Aug 12, 2023
·
3 revisions
실험1
구동 화면 |
---|
구현 코드
class LogInViewController: UIViewController {
@IBOutlet weak var pwTextField: UITextField!
var diaryViewController: DiaryViewController?
override func viewDidLoad() {
super.viewDidLoad()
diaryViewController = self.storyboard?.instantiateViewController(withIdentifier: "diary") as? DiaryViewController
}
@IBAction func tapLogInButton(_ sender: Any) {
guard let diaryViewController = diaryViewController else { return }
if pwTextField.text == UserDefaults.standard.string(forKey: "Password") {
diaryViewController.modalPresentationStyle = .fullScreen
present(diaryViewController, animated: true)
}
}
@IBAction func addNewPassword(_ sender: Any) {
// UserDefaults을 활용해 패스워드를 저장합니다.
UserDefaults.standard.set(pwTextField.text, forKey: "Password")
}
}
실험2-1
구동 화면 |
---|
kSecReturnData의 경우
class LogInViewController: UIViewController {
@IBOutlet weak var pwTextField: UITextField!
var diaryViewController: DiaryViewController?
override func viewDidLoad() {
super.viewDidLoad()
diaryViewController = self.storyboard?.instantiateViewController(withIdentifier: "diary") as? DiaryViewController
}
@IBAction func tapLogInButton(_ sender: Any) {
guard let diaryViewController = diaryViewController else { return }
// MARK: - Keychain을 활용해 패스워드를 호출합니다.
guard let data = KeychainManager.get(account: "홍대로모이시조") else { return }
let password = String(decoding: data, as: UTF8.self)
if pwTextField.text == password {
diaryViewController.modalPresentationStyle = .fullScreen
present(diaryViewController, animated: true)
pwTextField.text = ""
print("ReadPassword: \(password)")
}
}
@IBAction func addNewPassword(_ sender: Any) {
// MARK: - Keychain을 활용해 패스워드를 저장합니다.
do {
try KeychainManager.save(account: "홍대로모이시조", password: pwTextField.text?.data(using: .utf8) ?? Data())
pwTextField.text = ""
print("비밀번호 저장되었습니다.")
} catch {
print("Error입니다.")
}
}
}
class KeychainManager {
enum KeychainError: Error {
case duplicateEntry
case unkown(OSStatus)
}
static func save(account: String, password: Data) throws {
let query: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account as AnyObject,
kSecValueData as String: password as AnyObject,
]
let status = SecItemAdd(query as CFDictionary, nil)
if status == errSecDuplicateItem {
try update(account: account, password: password)
return
}
guard status == errSecSuccess else {
throw KeychainError.unkown(status)
}
print("save")
}
static func update(account: String, password: Data) throws {
let query: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account as AnyObject,
]
let newQuery: [String: AnyObject] = [
kSecValueData as String: password as AnyObject,
]
let status = SecItemUpdate(query as CFDictionary, newQuery as CFDictionary)
guard status == errSecSuccess else {
throw KeychainError.unkown(status)
}
print("update")
}
static func get(account: String) -> Data? {
let query: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
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("ReadStatus: \(status)")
return result as? Data
}
static func delete(account: String) {
let query: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account as AnyObject,
]
let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess else {
print("delete fail")
}
print("delete success")
}
}
kSecReturnAttributes의 경우
class LogInViewController: UIViewController {
@IBOutlet weak var pwTextField: UITextField!
var diaryViewController: DiaryViewController?
override func viewDidLoad() {
super.viewDidLoad()
diaryViewController = self.storyboard?.instantiateViewController(withIdentifier: "diary") as? DiaryViewController
}
@IBAction func tapLogInButton(_ sender: Any) {
guard let diaryViewController = diaryViewController else { return }
// MARK: - Keychain을 활용해 패스워드를 호출합니다.
guard let data = KeychainManager.get(account: "홍대로모이시조") else { return }
let password = String(decoding: data, as: UTF8.self)
if pwTextField.text == password {
diaryViewController.modalPresentationStyle = .fullScreen
present(diaryViewController, animated: true)
pwTextField.text = ""
print("ReadPassword: \(password)")
}
}
@IBAction func addNewPassword(_ sender: Any) {
// MARK: - Keychain을 활용해 패스워드를 저장합니다.
do {
try KeychainManager.save(account: "홍대로모이시조", password: pwTextField.text?.data(using: .utf8) ?? Data())
pwTextField.text = ""
print("비밀번호 저장되었습니다.")
} catch {
print("Error입니다.")
}
}
}
class KeychainManager {
enum KeychainError: Error {
case duplicateEntry
case unkown(OSStatus)
}
static func save(account: String, password: Data) throws {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: password,
]
let status = SecItemAdd(query as CFDictionary, nil)
if status == errSecDuplicateItem {
try update(account: account, password: password)
return
}
guard status == errSecSuccess else {
throw KeychainError.unkown(status)
}
print("save")
}
static func update(account: String, password: Data) throws {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
]
let newQuery: [String: Any] = [
kSecValueData as String: password,
]
let status = SecItemUpdate(query as CFDictionary, newQuery as CFDictionary)
guard status == errSecSuccess else {
throw KeychainError.unkown(status)
}
print("update")
}
static func get(account: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnAttributes as String: true
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == errSecSuccess, let attributes = result as? [String: Any],
let passwordData = attributes[kSecValueData as String] as? Data {
return passwordData
}
return nil
}
static func delete(account: String) {
let query: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account as AnyObject,
]
let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess else {
print("delete fail")
}
print("delete success")
}
}
실험2-2
-
검색을 설명하는 사전입니다. 일반적인 쿼리 사전은 다음으로 구성됩니다.
query
let account: String = "password" let password = pwTextFireld.text else { return } (비밀번호 값) var query: [String: Any] = [ kSecClass as String: kSecClassInternetPassword, kSecAttrAccount as String: account, kSecAttrServer as String: server, kSecValueData as String: passwor ]
- The item's class : Item Class Keys and Valus의 값 중 하나를 사용하여 원하는 아이템의 종류(예: 암호, 인증서 또는 암호화 키)를 지정합니다.
- Attributes : 찾은 항목이 가져야 하는 속성을 표시하여 검색 범위를 좁힙니다. 지정하는 속성이 많을수록 결과가 더 세분화되지만 모든 항목 클래스에 모든 속성이 적용되는 것은 아닙니다. 가능한 속성의 전체 목록은 Item Attribute Keys and Values를 참조하십시오.
- Search parameters : 다양한 방법으로 검색 조건을 지정합니다. 예를 들어 결과를 특정 수의 항목으로 제한하거나, 문자열 속성을 일치시킬 때 대소문자 구분을 제어하거나, 특정 항목 세트 중에서만 검색할 수 있습니다. 가능한 검색 매개변수의 전체 목록은 Search Attribute Keys and Values를 참조하십시오.
- One or more return types : Item Return Result Keys에 있는 키를 사용하여 항목의 속성, 항목의 데이터, 데이터에 대한 참조, 데이터에 대한 영구 참조 또는 이들의 조합을 찾는지 여부를 나타냅니다.둘 이상의 반환 유형을 지정하면 요청한 각 유형이 포함된 사전이 검색에서 반환됩니다. 검색에서 여러 결과가 허용되면 모두 항목 배열로 함께 반환됩니다.
-
Item Class Keys
- kSecClass
- 저장하려는 데이터의 종류를 의미합니다.
- kSecAttr
- 저장할 데이터의 종류를 지정해줍니다. 저장된 데이터를 검색할 때 필요한 정보입니다.
- kSecValueData
- 업데이트 할 값(Value)
- kSecReturnAttrubutes
- 값이 항목 속성을 반환할지 여부를 나타내는 값 입니다.
- kSecClass
- 반환 시 찾은 항목에 대한 참조입니다. 결과의 정확한 유형은 Item Return Result Keys에서 설명한 대로 속성에 제공된 값을 기반으로 합니다.
구동 화면 |
---|
class UpdatePasswordViewController: UIViewController {
@IBOutlet weak var oldPassword: UITextField!
@IBOutlet weak var newPassword: UITextField!
@IBAction func updatePassword(_ sender: UIButton) {
guard let data = KeychainManager.get(account: "홍대로모이시조") else { return }
let password = String(decoding: data, as: UTF8.self)
guard password == oldPassword.text else { return }
try! KeychainManager.update(account: "홍대로모이시조", password: newPassword.text!.data(using: .utf8)!)
}
}