Cleanup the existing code

This commit is contained in:
Benoit Marty 2021-05-11 11:12:27 +02:00 committed by Benoit Marty
parent d81d971ce0
commit f31c44963b
2 changed files with 12 additions and 62 deletions

View file

@ -71,7 +71,7 @@ internal class RealmKeysUtils @Inject constructor(context: Context,
val encodedKey = Base64.encodeToString(key, Base64.NO_PADDING) val encodedKey = Base64.encodeToString(key, Base64.NO_PADDING)
val toStore = secretStoringUtils.securelyStoreString(encodedKey, alias) val toStore = secretStoringUtils.securelyStoreString(encodedKey, alias)
sharedPreferences.edit { sharedPreferences.edit {
putString("${ENCRYPTED_KEY_PREFIX}_$alias", Base64.encodeToString(toStore!!, Base64.NO_PADDING)) putString("${ENCRYPTED_KEY_PREFIX}_$alias", Base64.encodeToString(toStore, Base64.NO_PADDING))
} }
return key return key
} }
@ -84,7 +84,7 @@ internal class RealmKeysUtils @Inject constructor(context: Context,
val encryptedB64 = sharedPreferences.getString("${ENCRYPTED_KEY_PREFIX}_$alias", null) val encryptedB64 = sharedPreferences.getString("${ENCRYPTED_KEY_PREFIX}_$alias", null)
val encryptedKey = Base64.decode(encryptedB64, Base64.NO_PADDING) val encryptedKey = Base64.decode(encryptedB64, Base64.NO_PADDING)
val b64 = secretStoringUtils.loadSecureSecret(encryptedKey, alias) val b64 = secretStoringUtils.loadSecureSecret(encryptedKey, alias)
return Base64.decode(b64!!, Base64.NO_PADDING) return Base64.decode(b64, Base64.NO_PADDING)
} }
fun configureEncryption(realmConfigurationBuilder: RealmConfiguration.Builder, alias: String) { fun configureEncryption(realmConfigurationBuilder: RealmConfiguration.Builder, alias: String) {

View file

@ -58,23 +58,19 @@ import javax.security.auth.x500.X500Principal
* is not available. * is not available.
* *
* <b>Android [K-M[</b> * <b>Android [K-M[</b>
* For android >=KITKAT and <M, we use the keystore to generate and store a private/public key pair. Then for each secret, a * For android >=L and <M, we use the keystore to generate and store a private/public key pair. Then for each secret, a
* random secret key in generated to perform encryption. * random secret key in generated to perform encryption.
* This secret key is encrypted with the public RSA key and stored with the encrypted secret. * This secret key is encrypted with the public RSA key and stored with the encrypted secret.
* In order to decrypt the encrypted secret key will be retrieved then decrypted with the RSA private key. * In order to decrypt the encrypted secret key will be retrieved then decrypted with the RSA private key.
* *
* <b>Older androids</b>
* For older androids as a fallback we generate an AES key from the alias using PBKDF2 with random salt.
* The salt and iv are stored with encrypted data.
*
* Sample usage: * Sample usage:
* <code> * <code>
* val secret = "The answer is 42" * val secret = "The answer is 42"
* val KEncrypted = SecretStoringUtils.securelyStoreString(secret, "myAlias", context) * val KEncrypted = SecretStoringUtils.securelyStoreString(secret, "myAlias")
* //This can be stored anywhere e.g. encoded in b64 and stored in preference for example * //This can be stored anywhere e.g. encoded in b64 and stored in preference for example
* *
* //to get back the secret, just call * //to get back the secret, just call
* val kDecrypted = SecretStoringUtils.loadSecureSecret(KEncrypted!!, "myAlias", context) * val kDecrypted = SecretStoringUtils.loadSecureSecret(KEncrypted, "myAlias")
* </code> * </code>
* *
* You can also just use this utility to store a secret key, and use any encryption algorithm that you want. * You can also just use this utility to store a secret key, and use any encryption algorithm that you want.
@ -91,7 +87,6 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
private const val FORMAT_API_M: Byte = 0 private const val FORMAT_API_M: Byte = 0
private const val FORMAT_1: Byte = 1 private const val FORMAT_1: Byte = 1
private const val FORMAT_2: Byte = 2
} }
private val keyStore: KeyStore by lazy { private val keyStore: KeyStore by lazy {
@ -113,15 +108,14 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
/** /**
* Encrypt the given secret using the android Keystore. * Encrypt the given secret using the android Keystore.
* On android >= M, will directly use the keystore to generate a symmetric key * On android >= M, will directly use the keystore to generate a symmetric key
* On android >= KitKat and <M, as symmetric key gen is not available, will use an symmetric key generated * On android >= Lollipop and <M, as symmetric key gen is not available, will use an symmetric key generated
* in the keystore to encrypted a random symmetric key. The encrypted symmetric key is returned * in the keystore to encrypted a random symmetric key. The encrypted symmetric key is returned
* in the bytearray (in can be stored anywhere, it is encrypted) * in the bytearray (in can be stored anywhere, it is encrypted)
* On older version a key in generated from alias with random salt.
* *
* The secret is encrypted using the following method: AES/GCM/NoPadding * The secret is encrypted using the following method: AES/GCM/NoPadding
*/ */
@Throws(Exception::class) @Throws(Exception::class)
fun securelyStoreString(secret: String, keyAlias: String): ByteArray? { fun securelyStoreString(secret: String, keyAlias: String): ByteArray {
return when { return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> encryptStringM(secret, keyAlias) Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> encryptStringM(secret, keyAlias)
else -> encryptString(secret, keyAlias) else -> encryptString(secret, keyAlias)
@ -132,7 +126,7 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
* Decrypt a secret that was encrypted by #securelyStoreString() * Decrypt a secret that was encrypted by #securelyStoreString()
*/ */
@Throws(Exception::class) @Throws(Exception::class)
fun loadSecureSecret(encrypted: ByteArray, keyAlias: String): String? { fun loadSecureSecret(encrypted: ByteArray, keyAlias: String): String {
return when { return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> decryptStringM(encrypted, keyAlias) Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> decryptStringM(encrypted, keyAlias)
else -> decryptString(encrypted, keyAlias) else -> decryptString(encrypted, keyAlias)
@ -180,7 +174,7 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
- Store the encrypted AES - Store the encrypted AES
Generate a key pair for encryption Generate a key pair for encryption
*/ */
fun getOrGenerateKeyPairForAlias(alias: String): KeyStore.PrivateKeyEntry { private fun getOrGenerateKeyPairForAlias(alias: String): KeyStore.PrivateKeyEntry {
val privateKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.PrivateKeyEntry) val privateKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.PrivateKeyEntry)
if (privateKeyEntry != null) return privateKeyEntry if (privateKeyEntry != null) return privateKeyEntry
@ -205,7 +199,7 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
} }
@RequiresApi(Build.VERSION_CODES.M) @RequiresApi(Build.VERSION_CODES.M)
fun encryptStringM(text: String, keyAlias: String): ByteArray? { private fun encryptStringM(text: String, keyAlias: String): ByteArray {
val secretKey = getOrGenerateSymmetricKeyForAliasM(keyAlias) val secretKey = getOrGenerateSymmetricKeyForAliasM(keyAlias)
val cipher = Cipher.getInstance(AES_MODE) val cipher = Cipher.getInstance(AES_MODE)
@ -229,7 +223,7 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
return String(cipher.doFinal(encryptedText), Charsets.UTF_8) return String(cipher.doFinal(encryptedText), Charsets.UTF_8)
} }
private fun encryptString(text: String, keyAlias: String): ByteArray? { private fun encryptString(text: String, keyAlias: String): ByteArray {
// we generate a random symmetric key // we generate a random symmetric key
val key = ByteArray(16) val key = ByteArray(16)
secureRandom.nextBytes(key) secureRandom.nextBytes(key)
@ -246,7 +240,7 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
return format1Make(encryptedKey, iv, encryptedBytes) return format1Make(encryptedKey, iv, encryptedBytes)
} }
private fun decryptString(data: ByteArray, keyAlias: String): String? { private fun decryptString(data: ByteArray, keyAlias: String): String {
val (encryptedKey, iv, encrypted) = format1Extract(ByteArrayInputStream(data)) val (encryptedKey, iv, encrypted) = format1Extract(ByteArrayInputStream(data))
// we need to decrypt the key // we need to decrypt the key
@ -307,22 +301,6 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
output.write(bos1.toByteArray()) output.write(bos1.toByteArray())
} }
// @RequiresApi(Build.VERSION_CODES.M)
// @Throws(IOException::class)
// fun saveSecureObjectM(keyAlias: String, file: File, writeObject: Any) {
// FileOutputStream(file).use {
// saveSecureObjectM(keyAlias, it, writeObject)
// }
// }
//
// @RequiresApi(Build.VERSION_CODES.M)
// @Throws(IOException::class)
// fun <T> loadSecureObjectM(keyAlias: String, file: File): T? {
// FileInputStream(file).use {
// return loadSecureObjectM<T>(keyAlias, it)
// }
// }
@RequiresApi(Build.VERSION_CODES.M) @RequiresApi(Build.VERSION_CODES.M)
@Throws(IOException::class) @Throws(IOException::class)
private fun <T> loadSecureObjectM(keyAlias: String, inputStream: InputStream): T? { private fun <T> loadSecureObjectM(keyAlias: String, inputStream: InputStream): T? {
@ -443,32 +421,4 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
return bos.toByteArray() return bos.toByteArray()
} }
private fun format2Make(salt: ByteArray, iv: ByteArray, encryptedBytes: ByteArray): ByteArray {
val bos = ByteArrayOutputStream(3 + salt.size + iv.size + encryptedBytes.size)
bos.write(FORMAT_2.toInt())
bos.write(salt.size)
bos.write(salt)
bos.write(iv.size)
bos.write(iv)
bos.write(encryptedBytes)
return bos.toByteArray()
}
private fun format2Extract(bis: InputStream): Triple<ByteArray, ByteArray, ByteArray> {
val format = bis.read()
assert(format.toByte() == FORMAT_2)
val saltSize = bis.read()
val salt = ByteArray(saltSize)
bis.read(salt)
val ivSize = bis.read()
val iv = ByteArray(ivSize)
bis.read(iv)
val encrypted = bis.readBytes()
return Triple(salt, iv, encrypted)
}
} }