· 5 years ago · Apr 09, 2020, 10:08 PM
1package ru.sudox.android
2
3import android.content.Context
4import android.security.keystore.KeyGenParameterSpec
5import android.security.keystore.KeyProperties
6import android.util.Base64
7import ru.sudox.android.core.inject.APP_CONTEXT_NAME
8import java.security.KeyStore
9import javax.crypto.Cipher
10import javax.crypto.KeyGenerator
11import javax.crypto.spec.IvParameterSpec
12import javax.inject.Inject
13import javax.inject.Named
14import javax.inject.Singleton
15
16private const val DATABASE_KEY_ALIAS = "sudox_database_key_alias"
17private const val DATABASE_KEY_IV_PREF = "database_key_iv_pref"
18private const val DATABASE_KEY_PREF = "database_key_pref"
19
20@Singleton
21class AppEncryptor @Inject constructor(
22 @Named(APP_CONTEXT_NAME) val context: Context
23) {
24
25 private val sharedPreferences = context.getSharedPreferences("SUDOX_ENCRYPTOR_PREFS", Context.MODE_PRIVATE)
26 private val keyStore = KeyStore.getInstance("AndroidKeyStore").apply {
27 load(null)
28 }
29
30 /**
31 * Получает или генерирует ключ для шифрования базы данных.
32 * Сгенерированный ключ БД шифруется с помощью ключа, сохраненного в AndroidKeyStore.
33 *
34 * @return Ключ шифрования БД
35 */
36 fun getDatabaseKey(): ByteArray {
37 val keyEntry = keyStore.getEntry(DATABASE_KEY_ALIAS, null) as? KeyStore.SecretKeyEntry
38
39 if (keyEntry != null) {
40 val encodedSecretKey = sharedPreferences.getString(DATABASE_KEY_PREF, null)
41
42 if (encodedSecretKey != null) {
43 val decodedIv = Base64.decode(sharedPreferences.getString(DATABASE_KEY_IV_PREF, null), Base64.DEFAULT)
44
45 return with(Cipher.getInstance("AES/CTR/NoPadding")) {
46 init(Cipher.DECRYPT_MODE, keyEntry.secretKey, IvParameterSpec(decodedIv))
47 doFinal(Base64.decode(encodedSecretKey, Base64.DEFAULT))
48 }
49 }
50 }
51
52 if (keyEntry != null) {
53 keyStore.deleteEntry(DATABASE_KEY_ALIAS)
54 }
55
56 val databaseSecretKey = with(KeyGenerator.getInstance("AES")) {
57 init(512)
58 generateKey()
59 }.encoded
60
61 val secretKey = with(KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")) {
62 init(KeyGenParameterSpec.Builder(DATABASE_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
63 .setBlockModes(KeyProperties.BLOCK_MODE_CTR)
64 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
65 .setKeySize(256)
66 .build())
67
68 generateKey()
69 }
70
71 val databaseKeyCipher = Cipher.getInstance("AES/CTR/NoPadding")
72 val encryptedDatabaseKey = with(databaseKeyCipher) {
73 init(Cipher.ENCRYPT_MODE, secretKey)
74 doFinal(databaseSecretKey)
75 }
76
77 sharedPreferences
78 .edit()
79 .putString(DATABASE_KEY_PREF, Base64.encodeToString(encryptedDatabaseKey, Base64.DEFAULT))
80 .putString(DATABASE_KEY_IV_PREF, Base64.encodeToString(databaseKeyCipher.iv, Base64.DEFAULT))
81 .apply()
82
83 return databaseSecretKey
84 }
85}