Cleanup the existing code
This commit is contained in:
parent
d81d971ce0
commit
f31c44963b
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue