clamp
Clamp
clamp
글쓰기 관리
전체 방문자
오늘
어제
  • 분류 전체보기 (509)
    • IOS (85)
    • SwiftUI+TCA+Combine (9)
    • RxSwift + MVVM (56)
    • Clean Architecture (12)
    • SWIFT (56)
    • iOS - TDD (2)
    • 디자인패턴 (4)
    • CS (56)
      • 알고리즘 (29)
      • 운영체제 (15)
      • 자료구조 (2)
      • 네트워킹 (4)
      • 기타 (6)
    • 회고 (0)
    • Firebase (18)
    • SwiftUI (10)
    • iOS - UIKit (11)
    • iOS - 오픈소스 (6)
    • 코딩테스트 (166)
      • 프로그래머스 (164)
    • 정보처리기사 (14)
    • GitHub (2)
글쓰기 / 관리자

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • ㅅ
  • Swift
  • uikit
  • Q

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
clamp

Clamp

[iOS] - 키체인(Key Chain)
IOS

[iOS] - 키체인(Key Chain)

2023. 6. 26. 11:38

키체인

 

 키체인을 학습하게된 이유는  OAuth를 통해 소셜로그인을 하고 toke을 받게된다. 이를 저장, 활용하려 하지만, 유저디폴트에 저장하기엔 민감한 정보라 키체인을 학습하게 되었다.

 

UserDefault와 다른점?

UserDefault에도 데이터를 쉽게 저장할 수 있지만 단순히 .info파일에 키-값 쌍의 텍스트 형태로 저장하게된다. 그렇기 때문에 OS를 탈옥하면 내용물을 볼 수 있기 때문에 보안이 필요한 민감한 데이터를 저장하기에는 보안이슈등 어울리지 않는다. 이를 방지하기 위해 암호, APIToken, 알고리즘을 위한 value등은 KeyChain에 저장하는 것이 좋다.

 

그렇다면 KeyChain이란 무엇일까?

  •  암호화된 데이터베이스, 즉 데이터를 안전하게 저장할 수 있는 보관소이다.

 

KeyChain의 특징은??

  • 앱을 삭제하더라도 Data는 삭제되지 않는다.
  • 정보와 속성으로 구성된다.
  • iOS app은 단일 키체인에 접근할 수 있다.
  • 사용자 기기 잠금 상태에 따라 키체인 잠금 상태도 동일하게 유지된다. -> 장치를 lock하면 key chain도 잠기고, unlock하면 keychain역시 풀린다.
  • 같은 개발자가 개발한 앱이라면 여러 앱에서 키체인 정보를 공유할 수 있다.

 

KeyChain에 무엇을 저장할까?

  • 로그인 암호
  • 결제데이터
  • 암호화 알고리즘을 위한 키
  • 간단한 메모?
  • 등등? 

 

KeyChain Service API

 

 

KeyChain Service API로 민감한 데이터를 암호화, 복호화 하며 재사용하기 쉽고 안전하게 사용할 수 있도록 도와준다.

 

 

Key Chain Items

  • Key Chain에 정보를 저장하기 위해서 KeyChain Item을 사용해야 한다.
  • 저장하려는 정보와 함께 item에 접근성을 제어하고 검색이 가능하게끔 하는 등 공개된 여러 특성(Attirbute)를 제공해야 한다.

 특성들의 예는 다음과 같다.

키 : kSecClass  

값

  • kSecClassGenericPassword: 일반적인 비밀번호
  • kSecClassCertificate: 인증서
  • kSecClassIdentity: iD

참고: https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_class_keys_and_values

 

 

키: kSecAttrAccount

값:  데이터 저장을 위한 키 ex) com.devreels.authorization

 

키: kSecReturnData

값: 데이터를 리턴할지 true / false

 

키: kSecMatchLimit

값

  • kSecMatchLimitOne: 매치되는 1개의 데이터
  • kSecMatchIssuers: 값이 인증서 또는 ID발급자와 일치하는 데이터

참고: https://developer.apple.com/documentation/security/ksecmatchlimit

 

 

구현 코드 ( 키체인 매니저 )

struct Keychain: KeychainProtocol {
    
    // 주어진 query를 사용하여 Keychain에 항목을 추가한다.
    // query: Keychain에 추가할 항목에 대한 쿼리 파라미터
    // return: 작업의 성공 여부
    func add(_ query: [String: Any]) -> OSStatus {
        // Keychain에 항목을 추가하는 메서드.. query 정보를 기반으로 keychain에 새로운 항목이 추가된다.
        return SecItemAdd(query as CFDictionary, nil)
    }
    
    // Keychain에서 항목을 검색하는 메서드, query: 검색할 항목에 대한 쿼리 파라미터
    // 리턴은 Data or nil
    func search(_ query: [String: Any]) -> Data? {
        var item: CFTypeRef? // keychain에서 검색된 항목을 담을 변수
        // query를 기반으로 keychain에서 검색, &item을 전달하여 검색 결과를 item에 저장, status에는 검색의 성공 여부와 관련된 상태코드가 담긴다.
        let status = SecItemCopyMatching(query as CFDictionary, &item)
        return status == noErr ? (item as? Data) : nil // status가 성공이라면 item을 Data타입으로 캐스팅하여 리턴.
    }
    
    // query를 사용해 Keychain의 항목을 업데이트. attribute: 업데이트할 속성을 포함한 쿼리파라미터
    func update(_ query: [String: Any], with attributes: [String: Any]) -> OSStatus {
        return SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
    }
    
    // query를 사용하여 Keychain에서 항목을 삭제, OSStatus로 삭제 작업의 성공 여부를 리턴
    func delete(_ query: [String: Any]) -> OSStatus {
        return SecItemDelete(query as CFDictionary)
    }
}
enum KeychainKey: String {
    case authorization = "com.devreels.authorization"
}

struct KeychainManager: KeychainManagerProtocol {

    var keychain: KeychainProtocol?

    func save(key: KeychainKey, data: Data) -> Bool {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key.rawValue, // 데이터 저장을 위한 키
            kSecValueData as String: data   //저장될 데이터를 Data Type으로
        ]

        let status = keychain?.add(query)
        return status == errSecSuccess ? true : false // status가 errSecSuccess로 저장에 성공하면 true, else false
    }

    func load(key: KeychainKey) -> Data? {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key.rawValue,
            kSecReturnData as String: true,             // 데이터를 리턴할지
            kSecMatchLimit as String: kSecMatchLimitOne //값이 일치하는 1개의 데이터만
        ]

        return keychain?.search(query)
    }

    func delete(key: KeychainKey) -> Bool {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key.rawValue
        ]

        let status = keychain?.delete(query)
        return status == errSecSuccess ? true : false
    }

    func update(key: KeychainKey, data: Data) -> Bool {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key.rawValue
        ]

        let attributes: [String: Any] = [
            kSecAttrAccount as String: key.rawValue,
            kSecValueData as String: data
        ]

        let status = keychain?.update(query, with: attributes)
        return status == errSecSuccess ? true : false
    }
}

 

저작자표시 비영리 동일조건 (새창열림)
    'IOS' 카테고리의 다른 글
    • [iOS] Modul
    • [iOS] Moya(Network)
    • [iOS] Static/Dynamic Library
    • [iOS] Class의 성능을 향상시킬 수 있는 방법들
    clamp
    clamp
    주니어 iOS개발자의 발악!!!!!!!

    티스토리툴바