Android Encrypted Shared Preferance

JhoonP·2023년 1월 26일
1

기존 SharedPreference의 문제점

기본 SharedPreference는 key, value값을 암호화하지 않고 그대로 파일에 저장하기 때문에 민감한 데이터를 저장하는 목적으로 사용할 수 없다.

따라서 암호화를 하여 저장해야 하는데 암/복호화에 사용하는 key 값을 안전하게 관리하는 것에는 어려움이 많다고 한다.
Android에서는 이러한 key를 하드웨어 단에서 보호되는 안전한 컨테이너를 이용하여 생성/사용할 수 있도록 Android Keystore 시스템을 제공하고 있다

API for Android Keystore

API를 사용해 Android Keystore에게 암/복호화에 사용할 masterkey 생성을 요청할 수 있다. masterkey는 생성 후 Android Keystore에 저장되고, 해당 키는 application process에서 직접 사용하는 것이 아닌 KeyStore.Entry interface와 같은 방법을 사용해 간접적으로 사용한다(내가 이해한 바로는...).

API level 18부터는 Android Keystore provider API를 제공하여 개별 app이 각각 credential을 가져 앱 내부에서만 사용하고 외부 접근으로부터 보호하도록 할 수 있는듯 하다. 따라서 앱 삭제 시 Android Keystore에 저장된 key 값도 함께 삭제해주는 등 사용성과 보안성을 증대시켰다.

앞의 provider를 사용하여 Android Keystore를 사용할 수도 있으나, 결국 앱 개발자들이 가장 많이 사용하는 목적은 File, SharedPreference 암/복호화 이다.

provider를 사용하는데 필요한 boilerplate code를 제거하고, 기존의 File, SharedPreference의 API interface를 그대로 유지하면서 암호화 기능까지 사용할 수 있도록 API level 23부터는 Android Jetpack에서 Security library를 제공한다.

Secure Library

Security library는 다음과 같은 방식으로 암/복호화를 수행한다고 한다.

  • File, SharedPreference 데이터를 암호화하는데 사용한 key들을 keyset에 보관한다. keyset은 SharedPreferance에 저장된다.
  • Android Keystore로 생성한 primary(master) key를 사용해 모든 keyset들을 암호화한다.
  • 데이터도 마찬가지로 암호화된다.

실제로 EncryptedSharedPreference를 사용한 뒤, 내부 저장소에서 해당 shared preference 파일을 확인하면 아래와 같은 내용을 확인할 수 있다.

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="__androidx_security_crypto_encrypted_prefs_key_keyset__">12a901d15f8baa24b9e88a9e68f3eb32ad1136cb4af22776b91caea9459bdd5a76d6992b57e268408944152c70171556e4fa065b13160a6951b75a32dc8172d6ee9cbf8b9828f7cc80d68c23794340221ad89ac94306ec5848af9d9c6e6372fdfd42d745be4b4a6d532adc365491e249e92503b64743defab475808f196b90842cabb874cce6a934d0f646f9d9f285814ded11f3d081a72abeb09d6a1849b7768b9e9899601c59797868b0a71a4408beb9cea707123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e4165735369764b6579100118beb9cea7072001</string>
    <string name="AXTznL5begfyWVUl+GcGajk7xoVmW0h2DCtZudxTAghw8BjV">ARdDw2jfHocuzA3PXT73sMwCNATF/f+ZNWWcG6mgY0mhuMvLz9hGLys=</string>
    <string name="AXTznL6Ev8dJ1E4Ws4dwJfyeDUQgK077hhUwLNnCt+3opw==">ARdDw2g859mf9i8UgWzWHvbZbBQytmCdcaXBVQgfy6+Qq/eIAs4=</string>
    <string name="__androidx_security_crypto_encrypted_prefs_value_keyset__">128801aa5d8f25163f993b8f95a2a874a1353f227ffca6f2e5ee1dad9243b8b3a1ea16531bfa8a10da864655c9b33e865bf072eafcdad659912a5198bf32218f186c02ff3efff27338a50617f194c3fd8b06ebfad829c3a53dbfd8b221fa7f9103ad8e8ad1583250b5c1779b20b618e46a3edb0b50d3a3bdc8c7ace84c21a69f36d4996f9e7fe81e8bf01b1a4408e8868fba01123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e41657347636d4b6579100118e8868fba012001</string>
    <string name="AXTznL4uW8pTacv6rnmZqMoGZHNWTJqwuD8q0XFlWnvw">ARdDw2gTVJYJ3PCEaYgwoZjKMCEAFAv7aLRACnmcKGyjRaHYgeBJZJE=</string>
    <string name="AXTznL5AE+UHfloGh5ZBZ+3iZL2hplrdIffsC3I/qA==">ARdDw2gRmqy+haYjIRDGML/ai32T5alKEQKaqlJDRhfQHcUIzthVAQo=</string>
</map>

key와 value가 암호화되어 저장되어 있는것 같긴 한데, 어떤 방식으로 저장됐는지 자세하게 설명해준 문서를 따로 찾지 못해서 구현 코드를 찾아서 정리해보았다.

암호화

  1. key를 keyset handler(key용)를 사용해 결정적 암호화 알고리즘으로 암호화
  2. 1.에서 얻은 암호화된 key를 사용해 value를 비결정적 암호화 알고리즘으로 암호화
  3. 1~2에서 얻은 암호화된 key, value를 shared preference에 저장

복호화

  1. key를 keyset handler(value용)로 암호화
  2. shared preference에서 1.에서 얻은 암호화된 key와 매칭되는 value를 획득
  3. 2.에서 얻은 value를 암호화된 key를 사용해 복호화

__androidx_security_crypto_encrypted_prefs_key_keyset__ item의 경우 key를 암/복호화 하는데 사용하는 keyset의 key/value item인 것 같은데 여기 까지는 파지는 못했다... 해당 이름을 key로 사용하는걸 막고 있기는 한 것 같다.


내부 원리를 알기 전까진 "keystore를 사용해 암호화한다" 까지만 알고 있었어서 app signing시 사용하는 .keystore를 사용해 어찌저찌 암호화를 하나보다~ 라고 안채로 심지어 다른 사람에게도 그렇게 이야기 했었는데 둘이 전혀 다른 개념이라는걸 알고나니 역시 제데로 공부해야 부끄러운 일이 없다...

참고자료

profile
배울게 끝이 없네 끝이 없어

0개의 댓글