diff --git a/008. KeyStore System and Security/Part 0_Keyword List(by Charlezz).md b/008. KeyStore System and Security/Part 0_Keyword List(by Charlezz).md new file mode 100644 index 0000000..914057f --- /dev/null +++ b/008. KeyStore System and Security/Part 0_Keyword List(by Charlezz).md @@ -0,0 +1,144 @@ +# Part 0 : Keyword List + +## Encryption/Decryption + +아직 암호화되지 않은 메시지를 평문(plaintext)이라한다. +암호화 된 메시지는 암호문(ciphertext)라고 한다. + +평문과 암호문 사이에서는 두개의 기능이 존재한다. +바로 암호화와 복호화다. + +암호화(Encryption)는 평문을 어떤 암호화 함수를 통해 암호문으로 바꾸는 것을 말한다. +복호화(Decryption)은 암호문을 어떤 복호화 함수를 통해 평문으로 바꾸는 것을 말한다. + +### 암호의 작동방식 + +암호는 다음과 같은 두개의 주된 구성요소로 이루어진다. + +- 치환(permutation) : 주어진 한 항목을 변환하는 함수인데, 각 항목에 고유한 역원이 존재해야 한다는 조건이 붙는다. +- 운영 모드(mode of operation) : 치환을 이용해서 임의의 길이의 메시지를 처리하는 데 쓰이는 알고리즘이다. + +## 대칭 키 암호 + +대칭 키 암호는 암호화 알고리즘의 한 종류로, 암호화와 복호와에 같은 암호키를 쓰는 알고리즘을 의미 한다. + +대칭 키 암호에서는 암호화를 하는 측과 복호화를 하는 측이 같음 키를 공유해야 한다. 이러한 점이 비대칭키 암호화 방식과 다른점이며, 대부분의 대칭 키 암호는 비대칭 키 방식과 비교하여 계산 속도가 빠르다는 장점을 가진다. + +대칭 키 암호는 암호화하는 단위에 따라 블록 암호와 스트림 암호로 나눌 수 있다. + +- 블록 암호 : 정해진 한 단위(블록)을 입력 받아 암호화 블럭을 생성 +- 스트림 암호 : 연속적인 비트/바이트를 입력받아 암호화 비트/바이트를 생성 + +### 블록 암호 + +블록 암호는 암호문을 만들기 위하여 암호 키와 알고리즘이 데이터 블록 단위로 적용되는 암호화 방법이다. 평문의 동일 블록들이 하나의 메시지에서 동일한 암호문으로 되지 않도록 하기 위해 이전 암호 블록의 암호문을 다음 블록에 순서대로 적용하는 것이다. 라운드 함수를 사용해 반복적으로 암호화 과정을 수행하는 것으로 암호화 강도를 향상시킨다. 블록 암호는 전치와 대체를 이용한다. + +블록암호는 암호화 알고리즘 하나와 복호화 알고리즘 하나로 구성된다. 암호화 알고리즘은 키와 평문을 입력받아 암호문을 산출하고, 복호화 알고리즘은 암호화 알고리즘의 역에 해당하며 암호문을 평문으로 복원한다. 암호화 알고리즘과 복호화 알고리즘은 서로의 역이므로, 두 알고리즘은 비슷한 연산들로 구성되는 경우가 많다. + +블록암호 구조에는 페이스텔 구조와 SPN구조가 있다. + +- [페이스텔](http://wiki.hash.kr/index.php?title=페이스텔&action=edit&redlink=1)(Feistel) 구조: 암호화와 복호화 과정이 동일하다. 또한, 평문을 좌, 우로 나누어서 뒤집고, [XOR](http://wiki.hash.kr/index.php/XOR)과 같은 과정을 지나 혼돈과 확산을 충족한다. + +- [SPN](http://wiki.hash.kr/index.php?title=SPN&action=edit&redlink=1)(Substitution-Permutation Network) 구조: [대체](http://wiki.hash.kr/index.php?title=대체&action=edit&redlink=1)(substitution)와 [치환](http://wiki.hash.kr/index.php?title=치환&action=edit&redlink=1)(permutation)을 이용하여 연산을 실행하다. 병렬연산이 가능하여 페이스텔 구조에 비해 연산속도가 빠르지만 복호화 시에 복호화 루틴을 별도로 구현하여야 한다. + +#### 블록암호의 특징 + +블록 암호의 특징은 다음과 같다. + +- 소프트웨어적으로 구현 가능하다. +- 전치와 치환을 반복하여 평문과 암호문으로부터 키에 대한 정보를 찾기 어렵게 한다. +- 데이터 전송, 대용량 데이터 저장 시 사용할 수 있다. +- 느린 암호화 속도 +- 에러 전파의 문제 +- 데이터의 크기가 작을 경우 효율적으로 암호화하기에는 적합하지 않다. + +#### 블록암호의 종류 + +- DES : + DES는 평문을 64비트로 나눠 56비트의 키를 이용하여 다시 64비트의 암호문을 만들어 내는 암호 알고리즘이다. 이때 암호문은 16번의 반복을 통해 만들어지는데 이때 16번의 반복동안 라운드 함수를 적용하고 이때 라운드 함수에 적용되는 키는 라운드 키이다. 이때 라운드 키는 키 스케줄에 의해 라운드 키를 발생시킨다. 56비트 키는 오늘날 컴퓨팅 환경에서는 너무 짧다는 것이 문제다. 98년도에 무차별 공격방식으로 2일 내에 암호를 털어냈다. +- AES : + AES(Advanced Encryption Standard)는 DES의 암호화 강도가 약해지면서 개발되었으며 향후 30년 정도 사용할 수 있는 안정성, 128비트 암호화 블록, 다양한 키의 길이(128/192/256 비트)를 갖춘 대칭형 암호 알고리즘이다. 고급 암호화 표준이라고 한다. +- 아리아 +- 시드 +- 하이트 + +### 스트림 암호 + +블록 암호는 평문을 구성하는 비트들을 블록이라는 단위로 조각내서 키의 비트들과 뒤섞어서 같은 크기의 암호문 블록들을 산출한다. 그러한 블록은 흔히 64비트 또는 128비트다. 반면 스트림 암호는 평문의 비트들과 키의 비트들을 뒤섞지 않는다. 대신 스트림 암호는 키로부터 유사난수 비트들을 생성하고, 그것을 평문과 XOR해서 암호문을 생성한다. + +스트림 암호는 하드웨어 구현이 간편하며 속도가 빠르기 때문에 무선 통신 등의 환경에 주로 사용된다. + +#### 스트림 암호의 종류 (동기식 vs 비동기식) + +크게 보아서 스트림 암호는 상태 있는 스트림 암호(동기식)와 카운터 기반 스트림 암호(비동기식)로 나뉜다. + +스트림 암호의 난수열을 암호화할 입력값과 독립적으로 생성하는 경우를 동기식(synchronous) 스트림 암호로 부른다. 반대로 입력값이 난수열 생성에 영향을 끼치는 경우 비동기식(asynchronous) 혹은 자기 동기(self-synchronizing) 스트림 암호로 부른다. + +**동기식 스트림 암호** +동기식 스트림 암호는 난수열을 생성하기 위해 내부 상태(internal state)를 유지하며, 이전 내부 상태에서 새로운 내부 상태와 유사난수를 얻는다. 문자열의 암호화 및 복호화는 생성된 유사난수열과 입력값을 XOR하는 방식으로 이루어진다. + +동기식 스트림 암호에서는 암호화 및 복호화할 문자열에서 특정 위치 비트를 변경할 경우 암호화된 결과에서도 같은 위치 비트가 변경되며, 다른 위치의 비트는 변경되지 않는다. 따라서 암호화 문자열을 전송할 시에 특정 비트가 다른 값으로 손상되었어도 복호화 시 다른 비트에는 영향을 미치지 않는다. 하지만 전송 오류에서 비트가 사라지거나 잘못된 비트가 추가되는 경우 오류가 난 시점 이후의 복호화가 실패하게 되며, 따라서 전송 시에 동기화(synchronize)가 필요하다. + +또한, 같은 암호화 키로 여러 입력값을 사용할 수 있으면 이를 이용한 암호공격(cryptanalysis)이 가능하다. + +**비동기식 스트림 암호** +비동기식 스트림 암호는 난수열을 생성할 때 암호화 키와 함께 이전에 암호화된 문자열 일부를 사용한다. 이 암호의 내부 상태는 이전 내부 상태에 의존하지 않는다. 따라서 암호화 문자열을 전송할 시에 일부 비트가 값이 바뀌거나, 혹은 비트가 사라지고 추가되는 오류가 발생하여도, 일부분만이 복호화에 실패하며 그 이후에는 다시 정상적인 복호화 값을 얻을 수 있는 자기 동기성을 가진다. + +## 해시 함수 + +임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수를 말한다. 해시 함수에 얻어지는 값은 **해시값**, **해시코드**, **해시 체크섬** 또는 간단히 **해시**라고 한다. + +암호학적 해시 함수와 비암호학적 해시 함수를 잘 구분해야 한다. 비암호학적 해시 함수는 해시 테이블 같은 자료구조에 쓰이거나 우발적인 오류를 검출하는데 쓰인다. 즉, 보안성에 대한 것은 고려되지 않는다. 예를들어 CRC(cyclic redundancy check:순환 중복 검사)는 파일의 우발적인 수정을 검출하는 데 쓰이는 비암호학적 해시 함수이다. + +암호학적인 해시 함수는 암호의 보안성과는 조금 다른 개념이다. 암호들은 자료의 평문을 암호화해서 저장 또는 전송함으로써 그 자료를 다른 누군가가 알아내지 못하게 하는데 주력한다. 즉, 암호의 목적은 자료의 기밀성을 지키는 것이다. 그러나 해시 함수는 중간에 누군가가 자료를 수정했다면 그 사실을 알 수 있게 하고, 자료의 무결성을 지키는 것이 목적이다. 어떤 해시 함수가 안전하다는 것은 서로 다른 두 자료에 대해 항상 서로 다른 해시 값을 산출 하는 것이다. 그렇게 함으로써 그 자료의 식별자 역할을 한다. + +해시 함수의 가장 흔한 용도는 디지털 서명이 있다. 디지털 서명을 사용하는 프로그램은 서명할 메시지 자체가 아닌 그 메시지의 해시를 처리 한다. 이때 해시는 메시지의 식별자 역할을 한다. 따라서 메시지에서 비트 하나만 변경되어도 메시지의 해시 값이 이전과 완전히 달라진다. 이를 통해 메시지가 수정되지 않았음을 증명한다. 메시지의 해시를 서명하는 것은 메시지 자체를 서명하는 것만큼이나 안전하고, 빠르다. + +### MessageDigest + +MessageDigest 클래스는 SHA-1 또는 SHA-256과 같은 알고리즘의 기능을 제공한다. 임의 크기의 데이터를 가져와 고정 길이 해시 값을 출력하는 안전한 단방향 해시 함수다. + +이 클래스는 추상적이며, 그렇기 때문에 개발자는 이 MessageDigest 클래스에 정의된 메서드에 초점을 맞춰 사용하면 된다. + +자바 플랫폼에서의 모든 구현은 다음 표준 MessageDigest알고리즘을 지원한다. + +- MD5:128비트 해시 함수다. 주로 프로그램이나 파일이 원본 그대로인지를 확인하는 무결성 검사 등에 사용된다. 1996년에 설계상 결함이 발견되어 SHA-1과 같은 안전한 알고리즘을 사용할 것을 권장한다. MD5를 보안 관련 용도로 쓰는 것은 권장하지 않는다. +- SHA-1:임의의 길이의 입력데이터를 160비트의 출력데이터로 바꾼다. 1995년 개정판 FIPS PUB 180-1로 발표되었고 메시지 길이에 대한 상한이 존재한다. +- SHA-256:SHA 알고리즘의 한종류로 256비트로 구성되며 64자리 문자열을 반환한다. SHA-2 계열 중 하나로 블록체인에서 가장많이 채택하여 사용한다. + +### 파일의 위변조를 검증하기 위한 Checksum 얻는 예제 + +https://howtodoinjava.com/java/io/sha-md5-file-checksum-hash/ + +## MAC(Message Authentication Code) + +MAC 알고리즘은 메시지의 **무결성**과 **인증성**을 보호한다. 암호의 키를 알고 있으면 암호문을 복호화할 수 있듯이, MAC의 키를 알고 있으면 주어진 메시지가 수정된 적이 없음을 확인할 수 있다. + +### HMAC + +HMAC(hash-based MAC)은 해시 함수로 MAC을 구축하는 방법이다. MAC은 비밀 값 전위나 비밀 값 후위 구성으로 만든 MAC보다 안전하다. HMAC은 바탕 해시 함수에 충돌 저항성이 있다면 안전한 유사난수함수(PRF)를 산출한다. 보안 통신 프로토콜 IPSec, SSH, TLS는 모두 HMAC을 사용한다. + +예제 : https://wan-blog.tistory.com/24 + +### CMAC + +암호기반 MAC(cipher-based MAC)은 주어진 블록 암호(예:AES) 하나만으로 MAC을 만든다. HMAC보다는 덜 쓰이지만, CMAC은 IPSec 프로토콜 모음의 일부인 IKE(Internet Key Exchange) 프로토콜을 비롯한 여러 시스템에서 쓰인다. + +예제 : https://www.programcreek.com/java-api-examples/?api=org.bouncycastle.crypto.macs.CMac + +## 비대칭 키 암호 + +비대칭 키 암호화에서는 두 개의 키를 사용한다. 비대칭 키 암호는 **공개 키 암호**라고도 한다. + +- 공개 키(public key) : 암호화에 쓰이는 키, 모든이에게 제공 +- 개인 키(private key) : 복호화에 쓰이는 키, 비밀리에 보관. + +개인 키로부터 공개 키를 계산하는 것은 가능하지만, 공개 키로부터 개인 키를 계산하는 것은 불가능하다. 이것이 비대칭 키 암호화의 핵심 내용이다. 대칭키 암화화는 하나의 키를 통해 암호화를 하고 복호화는 암호화의 역이지만, 비대칭 키 암호화에서는 역방향으로의 계산이 불가능하다. + +- Diffie-Hallman : + 1976년 공개키 암호 방식을 최초로 제안한 [휫필드 디피](http://wiki.hash.kr/index.php/휫필드_디피)(Whitfield Diffe)와 [마틴 헬만](http://wiki.hash.kr/index.php/마틴_헬만)(Martin Hellman)이 발명한 암호화 알고리즘이다. 디피 헬만은 두 사용자 간에 공통의 암호화키를 공개키 암호 방식의 개념을 이용하여서 사전에 어떠한 비밀키 교환이 없이도 공중 통신망 환경에서 공동키를 안전하게 공유할 수 있는 방법을 제시한 최초의 비밀키 교환 프로토콜이다. 가장 오래된 공개키 암호화 시스템으로 이산로그 구조의 복잡함을 활용한 방식이다. 현대 암호학의 혁명으로 불리지만, [중간자 공격](http://wiki.hash.kr/index.php/중간자_공격)에 취약하다는 단점이 있다. +- RSA : + RSA는 로널드 리베스트(Ronald Rivest)와 아디 샤미르(Adi Shamir), 레너드 애들먼(Leonard Adleman) 등 3명의 수학자에 의해 개발된 알고리즘을 사용하는 인터넷 공개키 암호화 및 인증시스템이다. 3명의 이름 가운데 첫 글자를 모아 붙인 용어이다. 암호화뿐만 아니라 전자서명이 가능한 최초의 알고리즘으로 알려져 있다. RSA가 갖는 전자서명 기능은 인증을 요구하는 전자 상거래 등에 RSA의 광범위한 활용을 가능하게 하였다. +- ECC : + 1985년 미국 워싱턴 대학교의 수학 교수인 닐 코블리츠(Neal Koblitz)와 IBM 연구소의 빅터 밀러(Victor Miller)가 거의 동시에 독립적으로 도출한 공개키 형식의 암호화 방식이다. 타원곡선이라고도 불리는 수식에 의해 정의되는 특수 가산법을 기반으로 암호화와 복호화를 수행하는 암호화 방식이다. 타원곡선 군의 연산에서 정의되는 이산대수 문제의 어려움의 이용을 기초로 하는 공개키 암호 알고리즘 시스템으로서 RSA/DSA와 같은 공개키 암호보다 짧은 키 길이와 빠른 연산속도를 가지면서 동일한 수준의 보안 강도를 제공하는 암호 알고리즘이다. 이와 같은 방식으로 만든 암호를 해독하는 방법은 아직 발견되지 않았다. 다만, 일부 곡선에서는 약점이 발견되어 실제 이 방식을 적용하려면 이것을 피할 연구가 필요하다. 짧은 키 사이즈로 높은 안전성이 보장되고, 또 서명할 때의 계산을 빠르게 할 수 있는것이 특징이다. 스마트카드(IC카드) 등 정보처리 능력이 그다지 높지 않은 기기에 이용하기에 적합한 암호화 방식이다 + diff --git a/008. KeyStore System and Security/Part 1_Jetpack Security.md b/008. KeyStore System and Security/Part 1_Jetpack Security.md new file mode 100644 index 0000000..3643bf5 --- /dev/null +++ b/008. KeyStore System and Security/Part 1_Jetpack Security.md @@ -0,0 +1,129 @@ +# Android Keystore와 Jetpack Security + +고전적인 방식의 암호화/복호화를 하는 예제를 먼저 살펴보자. + +![Warning This answer contains code you should not use as it is insecure](Warning This answer contains code you should not use as it is insecure.png) + +이 방식은 틀렸다. 왜냐하면 암호화 키가 노출되기 때문이다. 근본적으로는 Android Keystore를 사용해야 한다. + +## Android Keystore란? + +보안에서는 키 관리가 매우 중요한데, 기기의 분실 및 도난으로 부터 내 키를 보호하기 힘들다. 그렇기 때문에 Android Keystore를 사용해야한다. Android Keystore는 하드웨어 기반이므로 기기의 별도의 메모리 공간에서 동작한다. 앱이 키에 접근할 수 있어도 앱이 키 자료를 알 수는 없다. 사용자는 이 키를 조작할 수 없다. + +Android Keystore를 통한 기본적인 암/복호화는 다음과 같다. + +```kotlin +val message = "Hello World!" +val alias = "Charles" // 아무거나 지정해도 상관없음. + +// 키 생성 스펙 정의 +val keyGenParameterSpec = KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .setKeySize(256) + .build() + +// 키 생성기 생성 +val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore") +keyGenerator.init(keyGenParameterSpec) + +// 생성 된 키 +val secretKey: SecretKey = keyGenerator.generateKey() + +// 암호화 방식 정의 +val cipher = Cipher.getInstance("AES/GCM/NoPadding") +cipher.init(Cipher.ENCRYPT_MODE, secretKey) + +// 초기화 벡터 +val iv = cipher.iv + +// 암호화 된 메시지 로그로 출력해보기 +val encryptedData: ByteArray = cipher.doFinal(message.toByteArray()) +Log.e(TAG, "encryption = ${encryptedData.decodeToString()}") + +// 메세지를 복호화 하기 위해 안드로이드 키스토어를 가져옴 +val keyStore = KeyStore.getInstance("AndroidKeyStore") +keyStore.load(null) + +// 별칭(alias)을 통해 키를 가져옴 +val secretKeyEntry = keyStore.getEntry(alias, null) as KeyStore.SecretKeyEntry +val secretKey2: SecretKey = secretKeyEntry.secretKey + +// 복호화 방식 정의 (위 암호화 방식과 동일) +val cipher2 = Cipher.getInstance("AES/GCM/NoPadding") +val spec = GCMParameterSpec(128, iv) +cipher2.init(Cipher.DECRYPT_MODE, secretKey2, spec) + +// 복호화 된 메시지 로그로 출력해보기 +val decodedData: ByteArray = cipher2.doFinal(encryptedData) +Log.e(TAG, "decryption = ${decodedData.decodeToString()}") +``` + +## Jetpack Security + +Jetpack Security라이브러리는 파일 또는 데이터를 읽고 쓰는 과정에 대한 암호화/복호화 솔루션을 제공한다. + +Jetpack Security는 내부적으로 Tink와 Android Keystore를 사용한다. + +* Google Tink : 오용하기 쉽고 사용하기 어려운 암호화를 안전하고 그리고 올바르게 할 수 있도록 도와주는 라이브러리로 구글에서 관리하고 있다. 다중 언어, 교차 플랫폼 라이브러리로 안드로이드에 종속되지 않는다. + +Jetpack Security로 키를 만들 때 개발자는 보안과 관련된 내용을 상세히 알고 있지 않아도 된다. 또한 기기 별도의 하드웨어 부분인 스트롱 박스(Strong Box)라는 기능도 사용할 수 있다. 별도의 칩이므로 기술적인 면에서 약간 속도가 느릴 수 있으나 데이터 관리를 위한 매우 안전한 방법이고, 모든 작업이 별도의 메모리에서 이루어지므로 암호화/복호화 시 키가 앱으로 유출되지 않는다 + +Jetpack Security 에서는 키를 생성하여 키스토어를 입력 할 수 있는 마스터 키 클래스가 제공된다. 기본 설정으로 AES 256을 사용한다. GCM(예: MasterKeys.AES256_GCM_SPEC)을 사용하며 패딩을 사용하지 않는다. 안드로이드 앱개발자로서 유의해야 할 점은 반드시 키를 키스토어를 통해 만들고, 별칭(alias)을 통해 나중에 키를 찾을 수 있도록 한다. + +Jetpack Security를 통해 키를 생성하는 방식은 다음과 같다. + +```kotlin +// 앱 모듈의 build.gradle에 Jetpack Security 의존성 설정 +dependencies { + implementation("androidx.security:security-crypto:1.0.0") +} +``` + +```kotlin +// Jetpack Security사용하여 키 생성하기 +val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC +val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec) +``` + +getOrCreate의 내부를 살펴보면 위 예제 코드에서 KeyGenParameterSpec과 키를 생성하는 코드가 거의 동일한 것을 확인 할 수 있다. + +## EncryptedSharedPreferences + +일반적으로 SDK에서 제공하는 SharedPreferences 구현체를 사용하는 경우 데이터가 암호화 되지 않은 상태로 저장된다. 이를 보완하기 위해 Security라이브러리에서는 데이터 암호화를 기능을 추가한 SharedPreferences 구현체를 제공하는데 이것이 바로 EncryptedSharedPreferences다. + +사용법은 기존과 거의 동일하며, 초기화시에 암호화 알고리즘 및 암호화 키를 전달해야 한다. + +```java + String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC); + + SharedPreferences sharedPreferences = EncryptedSharedPreferences.create( + "secret_shared_prefs", // preferences 파일 이름 + masterKeyAlias, // 암호화 키 + context, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, // 키에 대한 암호화 방식 + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM // 값에 애한 암호화 방식 + ); + + // use the shared preferences and editor as you normally would + SharedPreferences.Editor editor = sharedPreferences.edit(); +``` + +## Datastore + +Datastore는 개선된 신규 데이터 저장소 솔루션으로, SharedPreferences를 대체한다. Kotlin 코루틴과 Flow를 기반으로 한 Datastore는 두 가지 구현, 즉 타입 객체를 저장하는 Proto Datastore(프로토콜 버퍼로 지원) 및 키-값 쌍을 저장하는 Preferences Datastore를 제공한다. + +SharedPreferences에는 UI 스레드에서 사용하기에 안전해 보일 수 있지만 실제로는 디스크 I/O 작업을 하는 동기적인 API다. 그러므로 ANR이 발생할 소지가 있는데, Datastore는 비동기적이고 일관된 트랜잭션 방식으로 데이터를 저장하여 SharedPreferences의 단점을 일부 극복한다. + +**DataStore의 특징** + +- 데이터 갱신을 트랜잭션 방식으로 처리 +- 데이터 현재 상태를 Flow로 표현 +- apply() 또는 commit()과 같은 data persistent 메서드가 없음 +- 변경 될 수 있는 참조를 내부상태에 반환하지 않음 +- Map 및 MutableMap 처럼 타입을 키로 하는 API를 제공함 + +다음의 도표를 통해 SharedPreferences와 DataStore의 차이점을 확인해보자. + +![SharedPreferences](SharedPreferences.png) + diff --git a/008. KeyStore System and Security/SharedPreferences.png b/008. KeyStore System and Security/SharedPreferences.png new file mode 100644 index 0000000..5b11801 Binary files /dev/null and b/008. KeyStore System and Security/SharedPreferences.png differ diff --git a/008. KeyStore System and Security/Warning This answer contains code you should not use as it is insecure.png b/008. KeyStore System and Security/Warning This answer contains code you should not use as it is insecure.png new file mode 100644 index 0000000..c4851b3 Binary files /dev/null and b/008. KeyStore System and Security/Warning This answer contains code you should not use as it is insecure.png differ