minaje

Keychain? UserDefault 쓰면 안돼? 본문

iOS

Keychain? UserDefault 쓰면 안돼?

minaje 2023. 5. 3. 01:23

Keychain? 

KeyChain은 Apple에서 적용하는 암호화된 데이터 저장 공간(encrypted database)이다.
KeyChain에는 비밀번호, 토큰 등 암호화 해야하는 데이터가 들어간다.

Keychain 동작원리

데이터를 Apple에서 제공하는 KeyChain services API 통해 암호화하고 KeyChain이라는 곳이 저장한다.
이때, Data 암호화(encryption)하여 패키징을 하게 되고, 암호화된 데이터에 접근하기 위한 Attributes 함께 패키징 된다.
그럼, 실제 데이터에 접근할 Attribites 데이터로 가져올 때는 자연스럽게 암호화된 데이터 해독 방식으로 있도록
Apple 기본 제공한다.

Keychain의 장점 

Keychain 특징을 알아보자!

  1.  Keychain은 앱은 삭제되더라도, Data는 삭제되지 않는다.
  2.  Keychain에는 잠금 기능이 있다. 잠금 되어 있으면 데이터에 접근 복호화 등 아무것도 하지 못한다.
  3.  같은 개발자가 구현한 앱이 여러개라면, 그 앱 사이의 Keychain 정보는 공유가 가능하다.

Keychain

데이터 종류

 

kSecClassGenericPassword : 일반 암호를 저장할 사용

kSecClassInternetPassword : 인터넷에서 불러온 암호를 저장할 사용

kSecClassCertificate : 인증서를 저장할 사용

kSecClassKey : 암호화 항목을 저장할 사용

kSecClassIdentity : ID 항목을 저장할 사용

참고자료

Keychain Create, Read, Delete 

실제로 진행 중인 프로젝트에 accessToken, refreshToken 관리하는 코드를 보도록 하자.

import UIKit
import Security

struct Const {
    struct KeychainKey {
        static let accessToken = "accessToken"
        static let refreshToken = "refreshToken"
        static let accessTokenExp = "accessTokenExp"
        static let refreshTokenExp = "refreshTokenExp"
        static let authority = "authority"
    }
}

class Keychain {
    func create(key: String, token: String) {
        let query: NSDictionary = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrAccount: key,
            kSecValueData: token.data(using: .utf8, allowLossyConversion: false) as Any
        ]
        SecItemDelete(query) // MARK: Keychain은 key 중복 값이 생기면 저장이 안되니 먼저 삭제를 해준다.

        let status = SecItemAdd(query, nil)
        assert(status == noErr, "failed to save Token")
    }

    func read(key: String) -> String? {
        let query: NSDictionary = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrAccount: key,
            kSecReturnData: kCFBooleanTrue as Any,  //MARK: CFData 타입으로 불러오라는 의미
            kSecMatchLimit: kSecMatchLimitOne       //MARK: 중복되는 경우, 하나의 값만 불러오라는 의미
        ]

        var dataTypeRef: AnyObject?
        let status = SecItemCopyMatching(query, &dataTypeRef)

        if status == errSecSuccess {
            if let retrievedData: Data = dataTypeRef as? Data {
                let value = String(data: retrievedData, encoding: String.Encoding.utf8)
                return value
            } else { return nil }
        } else {
            print("failed to loading, status code = (status)")
            return nil
        }
    }

    func delete(key: String) {
        let query: NSDictionary = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrAccount: key
        ]
        let status = SecItemDelete(query)
        assert(status == noErr, "failed to delete the value, status code = (status)")
    }
}

 

이렇게 Keychain 생성, 접근, 삭제할 있는 함수를 만들어줄 있다.

Keychain 넣기

func addKeychainToken() {
        self.keychain.create(
            key: Const.KeychainKey.accessToken,
            token: self.userData.accessToken
        )
        self.keychain.create(
            key: Const.KeychainKey.accessTokenExp,
            token: self.userData.accessTokenExp
        )
        self.keychain.create(
            key: Const.KeychainKey.refreshToken,
            token: self.userData.refreshToken
        )
        self.keychain.create(
            key: Const.KeychainKey.refreshTokenExp,
            token: self.userData.refreshTokenExp
        )
        self.keychain.create(
            key: Const.KeychainKey.authority,
            token: self.userData.authority
        )
    }

이런 식으로 간단하게 keychain 값을 넣을  있다!

self.keychain.read(key: Const.KeychainKey.authority)

접근도 간편하게 위와 같이 있다.

참고

https://mini-min-dev.tistory.com/114

'iOS' 카테고리의 다른 글

DI - Dependency Injection  (0) 2023.10.18
Swift의 정규식  (0) 2023.10.17
Swift Regex  (0) 2023.10.17