Using Jetpack Security for Data Encryption: Official Android Recommendations

Author: fundroid_Fang Zhuo

Link: https://blog.csdn.net/vitaviva/article/details/104828195

What is Jetpack Security?

Jetpack Security is the security component library released at Google I/O 2019. The Security library is simple and mainly includes two classes: EncryptedFile and EncryptedSharedPreferences, which are used to encrypt and decrypt the reading and writing of File and SharedPreferences respectively. Security requires a minimum SDK version of 23.

Using Jetpack Security for Data Encryption: Official Android Recommendations

Image Source: Google Developers

EncryptedFile encapsulates the logic of Google’s encryption library tink, providing FileInputStream and FileOutputStream for safer streaming reading and writing.

EncryptedSharedPreferences is a wrapper class for SharedPreferences that automatically encrypts key/value pairs in two ways:

  • Key encryption uses a deterministic encryption algorithm, allowing the key to be encrypted

  • Value encryption uses AES-256 GCM encryption, which is non-deterministic

Key Management

The Security library key management is divided into two parts:

  • Key set

Contains one or more keys to encrypt file or SharedPreferences data, stored in SharedPreferences.

  • Master Key

Used to encrypt all key sets, stored in the Android Keystore system.

Using the wrapper class MasterKeys of Android Keystore, you can create a Master Key with just two lines.

val keyGenParameterSpec = MasterKeys.AES256_GCM_SPECval masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
EncryptedSharedPreferences

1. Ordinary SharedPreferences

val data = getSharedPreferences("Sample", Context.MODE_PRIVATE)val editor = data.edit()editor.putInt("IntSave", 10)editor.apply()val intSaved = data.getInt("IntSave", 1)Log.d("IntSave", intSaved.toString())

The key and value are stored in plain text in xml

Using Jetpack Security for Data Encryption: Official Android Recommendations

Using Jetpack Security for Data Encryption: Official Android Recommendations

2. Using EncryptedSharedPreferences

val keyGenParameterSpec = MasterKeys.AES256_GCM_SPECval masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)val sharedPreferences = EncryptedSharedPreferences.create("Sample", masterKeyAlias, context, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)val editor = sharedPreferences.edit()editor.putInt("IntSave", 10)editor.apply()val intSaved = sharedPreferences.getInt("IntSave", 1)Log.d("IntSave", intSaved.toString())

The key and value are stored encrypted

Using Jetpack Security for Data Encryption: Official Android Recommendations

Using Jetpack Security for Data Encryption: Official Android Recommendations

3. Performance Comparison

3.1 SharedPreference TestCase

@RunWith(AndroidJUnit4::class)class SharedPreferenceTest {  private lateinit var data: SharedPreferences  private lateinit var editor: SharedPreferences.Editor  @Before  fun setup() {    // Context of the app under test.    val appContext = InstrumentationRegistry.getInstrumentation().targetContext        data = appContext.getSharedPreferences("Sample", Context.MODE_PRIVATE)    editor = data.edit()}  @Test  fun sharedPreference() {    for (i in 1..10000) {      editor.putInt("IntSave", i)      editor.apply()      val intSaved = data.getInt("IntSave", 1)      assertEquals(intSaved, i)    }  }}

3.2 EncryptedSharedPreference TestCase

@RunWith(AndroidJUnit4::class)class EncryptedSharedPreferenceTest {  private lateinit var data: SharedPreferences  private lateinit var editor: SharedPreferences.Editor  @Before  fun setup() {    val appContext = InstrumentationRegistry.getInstrumentation().targetContext    val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC    val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)    data = EncryptedSharedPreferences.create("Sample", masterKeyAlias, appContext, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)    editor = data.edit()}  @Test  fun encryptedSharedPreference() {    for (i in 1..10000) {      editor.putInt("IntSave", i)      editor.apply()      val intSaved = data.getInt("IntSave", 1)      Assert.assertEquals(intSaved, i)    }  }}

Using Jetpack Security for Data Encryption: Official Android Recommendations

The test results using pixel3 as shown above, the performance degrades by more than 10 times, but it is still good for an encryption library.

EncryptedFile

Write File

For example, write the string “MY SUPER SECRET INFORMATION” to a text file

val fileToWrite = "my_other_sensitive_data.txt"val encryptedFile = EncryptedFile.Builder(File(context.getFilesDir(), fileToWrite), context, masterKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build()// Write to a file.try {    val outputStream: FileOutputStream? = encryptedFile.openFileOutput()    outputStream?.apply {        write("MY SUPER SECRET INFORMATION".toByteArray(Charset.forName("UTF-8")))        flush()        close()    }} catch (ex: IOException) {    // Error occurred opening file for writing.}

Read File

Through EncryptedFile, you can output the plaintext “MY SUPER SECRET INFORMATION”;

Using only BufferedReader will output unreadable ciphertext (�]�}�Wr<������q1Bv����B��|)��j_��>��uBLN#���Y�w���;�̴?�w��M���;�K�M�Ƕ�

val fileToRead = "my_sensitive_data.txt"lateinit var byteStream: ByteArrayOutputStreamval encryptedFile = EncryptedFile.Builder(File(context.getFilesDir(), fileToRead), context, masterKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build()try {    encryptedFile.openFileInput().use { fileInputStream -&gt;        try {            val sb = StringBuilder()            val br = BufferedReader(InputStreamReader(fileInputStream) as Reader?)            br.readLine().forEach {                sb.append(it)            }            br.close()            // Output MY SUPER SECRET INFORMATION             Log.d("fileContents", sb.toString())        } catch (ex: Exception) {            // Error occurred opening raw file for reading.        } finally {            fileInputStream.close()        }    }} catch (ex: IOException) {    // Error occurred opening encrypted file for reading.}

Leave a Comment