Код: Выделить всё
class CryptographyManagerImpl : CryptographyManager {
private val KEY_SIZE_BITS: Int = 256
private val GCM_TAG_BITS: Int = 128
private val ANDROID_KEYSTORE = "AndroidKeyStore"
private val KEY_NAME = "MySecretKey"
private val ENCRYPTION_BLOCK_MODE = KeyProperties.BLOCK_MODE_GCM
private val ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_NONE
private val ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
@Throws(BiometricEncryptionException::class)
override fun getInitializedCypherForEncryption(): Cipher {
try {
val cipher = getCipher()
val secretKey = getOrCreateSecretKey(KEY_NAME)
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
return cipher
} catch (e: Exception) {
throw BiometricEncryptionException()
}
}
@Throws(BiometricEncryptionException::class)
@OptIn(ExperimentalEncodingApi::class)
override fun getInitializedCypherForDecryption(
encryptedData: String
): Cipher {
try {
val cipher = getCipher()
val secretKey = getOrCreateSecretKey(KEY_NAME)
val parts = encryptedData.split(":")
val iv = Base64.decode(parts[1], android.util.Base64.DEFAULT)
cipher.init(
Cipher.DECRYPT_MODE,
secretKey,
GCMParameterSpec(GCM_TAG_BITS, iv)
)
return cipher
} catch (e: Exception) {
throw BiometricEncryptionException()
}
}
@Throws(BiometricEncryptionException::class)
@OptIn(ExperimentalEncodingApi::class)
override fun encryptData(
data: String,
cipher: Cipher
): String {
try {
val byteArray = data.toByteArray(StandardCharsets.UTF_8)
val encryptedText = cipher.doFinal(byteArray)
val base64Text = Base64.encode(encryptedText, android.util.Base64.DEFAULT) + ":" + Base64.encode(cipher.iv, android.util.Base64.DEFAULT)
return base64Text
} catch (e: Exception) {
throw BiometricEncryptionException()
}
}
@Throws(BiometricEncryptionException::class)
@OptIn(ExperimentalEncodingApi::class)
override fun decryptData(
encryptedData: String,
cipher: Cipher
): String {
try {
val parts = encryptedData.split(":")
val encryptedText = Base64.decode(parts[0], android.util.Base64.DEFAULT)
val byteArray = cipher.doFinal(encryptedText)
val data = String(byteArray, StandardCharsets.UTF_8)
return data
} catch (e: Exception) {
throw BiometricEncryptionException()
}
}
private fun getCipher(): Cipher {
val transformation = "$ENCRYPTION_ALGORITHM/$ENCRYPTION_BLOCK_MODE/$ENCRYPTION_PADDING"
return Cipher.getInstance(transformation)
}
private fun getOrCreateSecretKey(keyName: String): SecretKey {
val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply {
load(null)
}
keyStore.getKey(keyName, null)?.let {
return it as SecretKey
}
val paramsBuilder = KeyGenParameterSpec.Builder(
keyName,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).apply {
setBlockModes(ENCRYPTION_BLOCK_MODE)
setEncryptionPaddings(ENCRYPTION_PADDING)
setKeySize(KEY_SIZE_BITS)
setUserAuthenticationRequired(true)
setInvalidatedByBiometricEnrollment(true)
}
val keyGenParams = paramsBuilder.build()
val keyGenerator = KeyGenerator.getInstance(
ENCRYPTION_ALGORITHM,
ANDROID_KEYSTORE
)
keyGenerator.init(keyGenParams)
return keyGenerator.generateKey()
}
}
Некоторые вещи, которые я заметил:
- При вызове getInitializedCypherForEncryption , если я проверю cipher.iv сразу после cipher.init, я всегда получаю 12-байтовый массив;
- Поскольку я использую BiometricPrompt и CryptoObject для дополнительной безопасности, если я снова проверю cipher.iv сразу после захвата проверенного зашифрованного объекта, IV изменится на 16-байтовый массив с другими значениями;
- Теперь getInitializedCypherForDecryption выдает вышеупомянутое исключение прямо в cipher.init, поскольку IV изменился сам по себе;
- Я попытался сохранить исходный 12-байтовый IV, чтобы повторно использовать его впоследствии, но полученные данные расшифровываются неправильно;
- Я также попытался сохранить исходный зашифрованный объект вместо передачи проверенного через CryptoObject, но я, очевидно, получаю исключение android.security.KeyStoreException: ключевой пользователь не аутентифицирован;
- Я также попытался сгенерировать свой собственный 12-байтовый IV и установил в getInitializedCypherForEncryption cipher.init. Та же проблема сохраняется, а также нарушает код для Android 16, поскольку установка IV вручную приводит к исключению.
Подробнее здесь: https://stackoverflow.com/questions/798 ... nly-12-byt