Jetpack Security とは
- Google I/O 2019で発表されたのセキュリティライブラリ
- 既存のAES暗号化・復号化に比べて安易
- 暗号化する際のパフォーマンスが優れている
-
EncryptedFile
、EncryptedSharedPreferences
がメインのクラスとしてあり、SharedPreferenceとファイルI/Oをサポート -
EncryptedFile
では googleが出してる暗号化ライブラリ tinkのロジックが内包されている。 - min SDK version 23
Master Keyの作成
Jetpack Securityは、キー管理に2部構成のシステムを使用します。
-
ファイルまたは共有設定データを暗号化するための 1つ以上のキーを含むキーセット。キーセット自体は SharedPreferences に格納されます。
-
すべてのキーセットを暗号化するマスターキー。このキーは Android キーストア システムを使用して保存されます。
-
Android Keystoreのラッパーclassである
MasterKeys
を使用するとたった2行で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())
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())
速度を比較してみる
SharedPreferenceTestのテストコード
@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のテストコード
@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)
}
}
}
測定結果 | EncryptedSharedPreferences | SharedPreferences |
---|---|---|
最遅 | 6212 | 567 |
平均 | 6119.3 | 514.3 |
最速 | 6028 | 426 |
単位 ms 実行環境 Pixel 3 |
比べてみると10倍の差が開いていますが、EncryptedSharedPreferencesも十分早いパフォーマンスが出ますね。
EncryptedFileを触ってみる
Write File
MY SUPER SECRET INFORMATION
という内容のtxtファイルを書き込んでみる。
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
MY SUPER SECRET INFORMATION
という内容がログに出力され、ちゃんと読める様になっている。
BufferedReaderだけで読み込むと、(�]�}�Wr<������q1Bv����B��|)��j_��>��uBLN#���Y�w���;�̴?�w��M���;�K�M�Ƕ�
と表示され、しっかりとencryptされているのがわかる。
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.
}