Shibuya.apk #35発表資料 Google I/O 2019にてJetpack Securityという新しいライブラリが発表されました。 なぜ必要だったか、そしてどれだけ簡単に使うことができるのかについてまとめました。
Jetpack Securityエムスリー株式会社 星川貴樹 (@oboenikui)Shibuya.apk #35
View Slide
Jetpack Security (androidx-security)● Google I/O 2019で発表された暗号系ライブラリ● 暗号化処理のベストプラクティスを実装しており、高い安全性かつ高速な処理を実現● Android 6.0+ で利用可能● 利用技術○ Android Keystore○ Tink (Google製の暗号ライブラリ)https://developer.android.com/jetpack/androidx/releases/security
登場背景 (想像)● Android Keystoreや暗号系の機能の敷居が高いという問題● ボイラープレートも多い● Android Keystoreが本格的に使えるようになったAndroid 6.0をminSdkVersionにするアプリが増えてくる頃だから?
暗号系機能の敷居が高い問題Android Keystoreの説明が最初の説明からちょっと分かりづらいAndroid Keystore システムを使用すると、コンテナに暗号化キーを格納し、デバイスからのキーの抽出をより困難にすることができます。キーストアに格納したキーは、エクスポート不可のキーマテリアルを使った暗号化処理に使用できます。https://developer.android.com/training/articles/keystore より引用
暗号系機能の敷居が高い問題Android Keystoreの説明が最初の説明からちょっと分かりづらいAndroid Keystore システムを使用すると、コンテナに暗号化キーを格納し、デバイスからのキーの抽出をより困難にすることができます。キーストアに格納したキーは、エクスポート不可のキーマテリアルを使った暗号化処理に使用できます。キー? キーマテリアル?https://developer.android.com/training/articles/keystore より引用
暗号系機能の敷居が高い問題ボイラープレート長い、設定項目もよくわからないval kpg: KeyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC,"AndroidKeyStore")val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(alias,KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY).run {setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)build()}kpg.initialize(parameterSpec)val kp = kpg.generateKeyPair()https://developer.android.com/training/articles/keystore より引用ECの鍵生成だけでこの長さ
暗号系機能の敷居が高い問題アルゴリズムの選択肢が多すぎてどれ選べばよいかわからない問題
暗号系機能の敷居が高い問題アルゴリズムの選択肢が多すぎ
暗号系機能の敷居が高い問題アルゴリズムの選択肢が多すぎhttps://developer.android.com/training/articles/keystore#SupportedCiphers より
Jetpack Securityで何が変わる?● 使うだけならAndroid Keystoreなどの事情を知る必要がない● 利用用途を限ることでシンプルに○ ファイルや設定の暗号化・復号の処理を短く書ける○ 暗号アルゴリズムの選択肢が今のところ1つだけなので困らない
使い方 - build.gradledependencies {def security_version = "1.0.0-alpha02"implementation "androidx.security:security-crypto:$security_version"}
使い方 - キーの生成val masterKeyAlias =MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
使い方 - キーの生成val masterKeyAlias =MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)推奨される設定が定数化されている &現バージョンでは他の選択肢がないので、迷わない
使い方 - ファイル暗号化val encryptedFile = EncryptedFile.Builder(File(path), context, masterKeyAlias,EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build()val result = runCatching {encryptedFile.openFileOutput().use {it.write(text.toByteArray(Charset.forName("UTF-8")))it.flush()}}
使い方 - ファイル暗号化val encryptedFile = EncryptedFile.Builder(File(path), context, masterKeyAlias,EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build()val result = runCatching {encryptedFile.openFileOutput().use {it.write(text.toByteArray(Charset.forName("UTF-8")))it.flush()}}先程生成したキーのエイリアス
使い方 - ファイル暗号化val encryptedFile = EncryptedFile.Builder(File(path), context, masterKeyAlias,EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build()val result = runCatching {encryptedFile.openFileOutput().use {it.write(text.toByteArray(Charset.forName("UTF-8")))it.flush()}}同じく選択肢はこれしか用意されていない
使い方 - ファイル暗号化val encryptedFile = EncryptedFile.Builder(File(path), context, masterKeyAlias,EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build()val result = runCatching {encryptedFile.openFileOutput().use {it.write(text.toByteArray(Charset.forName("UTF-8")))it.flush()}}通常のファイル読み書きとほぼ同じ記述で書ける
使い方 - ファイル復号val encryptedFile = EncryptedFile.Builder(File(path), context, masterKeyAlias,EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build()val result = runCatching {encryptedFile.openFileInput().use {it.bufferedReader().readText()}}
使い方 - SharedPreferenceval pref: SharedPreference =EncryptedSharedPreferences.create("pref_file_name",MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),context,EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)
使い方 - SharedPreferenceval pref: SharedPreference =EncryptedSharedPreferences.create("pref_file_name",MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),context,EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)いつものSharedPreferenceインターフェースを実装しているのであとは普段どおり使える
暗号化されたPreference XMLAV1M1P7NgVsTKwwfb4nesoj+Brz...12a901ba5467eb84...1288012427b544ed...
暗号化されたPreference XMLAV1M1P7NgVsTKwwfb4nesoj+Brz...12a901ba5467eb84...1288012427b544ed...Preferenceのkey, valueそれぞれを暗号化するための共通鍵を更に暗号化したもの
暗号化されたPreference XMLAV1M1P7NgVsTKwwfb4nesoj+Brz...12a901ba5467eb84...1288012427b544ed...key: foo, value: barが暗号化されたもの
今のところサポートしていないこと● SQLiteなど、その他のデータ暗号化● カスタマイズした認証(指紋認証など)画面
注意点● 自動バックアップを許可していると、他の端末でデータの不整合が起きる○ バックアップ除外設定をする必要あり● 将来的に推奨アルゴリズムが変更されるかも○ より強固なものへ「世代交代」していくんじゃないかと予想○ その際はマイグレーション処理が必要● まだ alpha02 なのでAPIが変わる可能性は高い
補足 - で、Android Keystoreって何?A. 暗号化・復号の処理をAndroidシステムに代行してもらう仕組み鍵のデータはAndroidシステムのプロセス以外からは参照できないようになっている。そのためアプリプロセス上で暗号化・復号処理はできないが、代わりにシステムプロセス上で暗号化・復号の処理を行ってくれる。プロセス間通信が行われるので、多分速くはない。
補足 - なぜ高速?A. プロセス間通信を最小限に留めているから (多分)Android Keystoreで暗号化・復号する場合プロセス間通信となるため少し遅くなる。Jetpack Securityでは、直接ファイルをKeystoreで暗号化するのではなく、別のAESキーを生成して暗号化や復号を行う。そのキーをKeystoreで暗号化し、SharedPreferenceに保存している。
補足 - Tinkとは?A. 多言語 & クロスプラットフォームな暗号ライブラリJava & Android, C++, Obj-C対応Go, JavaScript向けに開発中https://github.com/google/tink