Android官方推荐的安全组件:使用Jetpack Security为数据加密

2020-12-16 15:24:44 浏览数 (1)

作者:fundroid_方卓

链接:https://blog.csdn.net/vitaviva/article/details/104828195

Jetpack Security 是什么?

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

图片源:Google开发者

EncryptedFile 封装了Google的加密库tink的逻辑,提供FileInputStream和FileOutputStream,可以更安全的进行流的读写。

EncryptedSharedPreferences 是SharedPreferences包装类,通过两种方式自动加密键/值:

  • Key加密使用的是确定性的加密算法,使得秘钥可以被加密
  • Value加密使用AES-256 GCM加密,不确定加密

秘钥管理

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

  • 秘钥集合(Key set)

包含一个或多个秘钥来加密文件或SharedPreferences数据,存储在SharedPreferences中。

  • 主密钥(Master Key)

用来加密所有秘钥集合,存储在Android Keystore系统中

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

代码语言:javascript复制
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

EncryptedSharedPreferences

1,普通SharedPreferences

代码语言:javascript复制
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中

2,使用EncryptedSharedPreferences

代码语言:javascript复制
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被加密保存

3,性能对比

3.1 SharedPreference TestCase

代码语言:javascript复制
@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

代码语言:javascript复制
@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)
    }
  }
}

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

EncryptedFile

Write File

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

代码语言:javascript复制
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�Ƕ�

代码语言:javascript复制
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.
}

0 人点赞