使用Jetpack Security为数据加密

Jetpack Security 是什么?


Jetpack Security 是 Google I/O 2019 发布的安全组件库。Security构成简单,主要包含EncryptedFileEncryptedSharedPreferences两个类,分别用来对FileSharedPreferences的读写进行加密解密处理。Security要求min SDK version 23

  • EncryptedFile 封装了Google的加密库tink的逻辑,提供FileInputStreamFileOutputStream,可以更安全的进行流的读写。
  • EncryptedSharedPreferences 是SharedPreferences包装类,通过两种方式自动加密键/值:
    • Key加密使用的是确定性的加密算法,使得秘钥可以被加密
    • Value加密使用AES-256 GCM加密,不确定加密

秘钥管理


Security库秘钥管理分为两个部分:

  • 秘钥集合(Key set)
    包含一个或多个秘钥来加密文件或SharedPreferences数据,存储在SharedPreferences中。
  • 主密钥(Master Key)
    用来加密所有秘钥集合,存储在Android Keystore系统中

使用Android Keystore的包装类MasterKeys只用两行就可以制作Master Key。

val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

EncryptedSharedPreferences


普通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())

key和value都被明文保存在xml中

使用EncryptedSharedPreferences

    val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
    val 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())

key和value被加密保存

性能对比

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)
    }
  }
}

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)
    }
  }
}
性能比较 (ms) EncryptedSharedPreferences SharedPreferences
最慢 6212 567
平均 6119.3 514.3
最快 6028 426

使用pixel3的测试结果如上,性能上有10倍以上的劣化,但是作为加密库来说已经不错了。

EncryptedFile


Write File

例如向text文件中中写入 ”MY SUPER SECRET INFORMATION“字符串

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

通过EncryptedFile可以输出明文”MY SUPER SECRET INFORMATION“
仅使用BufferedReader则会输出不可读的密文(�]�}�Wr<������q1Bv����B��|)��j_��>��uBLN#���Y�w���;�̴?�w��M���;�K�M�Ƕ�

val fileToRead = "my_sensitive_data.txt"
lateinit var byteStream: ByteArrayOutputStream
val encryptedFile = EncryptedFile.Builder(
    File(context.getFilesDir(), fileToRead),
    context,
    masterKeyAlias,
    EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()

try {
    encryptedFile.openFileInput().use { fileInputStream ->
        try {
            val sb = StringBuilder()
            val br = BufferedReader(InputStreamReader(fileInputStream) as Reader?)
            br.readLine()
                .forEach {
                    sb.append(it)
                }
            br.close()
            // 输出 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.
}
发布了116 篇原创文章 · 获赞 15 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/vitaviva/article/details/104828195