Skip to content

Instantly share code, notes, and snippets.

@willyrh495
Created June 17, 2020 03:43
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save willyrh495/20fb2975dc71fe6f491365ce2c6c42d2 to your computer and use it in GitHub Desktop.
Save willyrh495/20fb2975dc71fe6f491365ce2c6c42d2 to your computer and use it in GitHub Desktop.
AES encryption using AndroidKeyStore
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import java.security.*
import javax.crypto.*
import javax.crypto.spec.GCMParameterSpec
import android.util.Base64
import androidx.annotation.VisibleForTesting
class EncryptionAES {
companion object {
private const val AES_MODE_M = "AES/GCM/NoPadding"
private const val KEY_ALIAS = "KeyAlias"
private const val ANDROID_KEY_STORE = "AndroidKeyStore"
}
private lateinit var keyStore: KeyStore
private var aesKey: Key
@VisibleForTesting
constructor(testKey: Key) {
aesKey = testKey
}
constructor() {
keyStore = KeyStore.getInstance(ANDROID_KEY_STORE)
keyStore.load(null)
generateEncryptKey()
aesKey = keyStore.getKey(KEY_ALIAS, null) as SecretKey
}
private fun generateEncryptKey() {
try {
if (!keyStore.containsAlias(KEY_ALIAS)) {
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE)
val keyGenParameterSpecBuilder = KeyGenParameterSpec.Builder(
KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
val keyGenParameter = keyGenParameterSpecBuilder
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false).build()
keyGenerator.init(keyGenParameter)
keyGenerator.generateKey()
}
} catch (e: KeyStoreException) {
e.printStackTrace()
}
}
fun encrypt(input: String): Pair<String, String> {
return try {
val aesIv = generateRandomIV()
val cipher = getCipherFromIv(aesIv, Cipher.ENCRYPT_MODE)
val encodedBytes: ByteArray = cipher.doFinal(input.toByteArray(Charsets.UTF_8))
Pair(aesIv, Base64.encodeToString(encodedBytes, Base64.DEFAULT))
} catch (e: Exception) {
e.printStackTrace()
Pair("", "")
}
}
private fun generateRandomIV(): String {
val random = SecureRandom()
val generated: ByteArray = random.generateSeed(12)
return Base64.encodeToString(generated, Base64.DEFAULT)
}
fun decrypt(publicIv: String, encrypted: String): String {
return try {
val decodedValue = Base64.decode(encrypted.toByteArray(Charsets.UTF_8), Base64.DEFAULT)
val cipher = getCipherFromIv(publicIv, Cipher.DECRYPT_MODE)
val decryptedVal: ByteArray = cipher.doFinal(decodedValue)
String(decryptedVal)
} catch (e: Exception) {
e.printStackTrace()
""
}
}
private fun getCipherFromIv(iv: String, cipherMode: Int): Cipher {
val cipher: Cipher = Cipher.getInstance(AES_MODE_M)
try {
val parameterSpec = GCMParameterSpec(128, Base64.decode(iv, Base64.DEFAULT))
cipher.init(cipherMode, aesKey, parameterSpec)
} catch (e: Exception) {
e.printStackTrace()
}
return cipher
}
}
import org.junit.Assert
import org.junit.Test
import java.security.SecureRandom
import javax.crypto.SecretKey
import javax.crypto.spec.SecretKeySpec
class EncryptionAESTest {
private val secureRandom = SecureRandom()
@Test
fun testEncryptionAES() {
val message = "the secret message"
val key = ByteArray(16)
secureRandom.nextBytes(key)
val secretKey: SecretKey = SecretKeySpec(key, "AES")
val encryption = EncryptionAES(secretKey)
val encrypted = encryption.encrypt(message)
val decrypted = encryption.decrypt(encrypted.first, encrypted.second)
Assert.assertEquals(message, decrypted)
}
@Test
fun testEncryptionAESFromTwoDifferentInstances() {
val message = "the secret message"
val key = ByteArray(16)
secureRandom.nextBytes(key)
val secretKey: SecretKey = SecretKeySpec(key, "AES")
val encryption = EncryptionAES(secretKey)
val encryption2 = EncryptionAES(secretKey)
val encrypted = encryption.encrypt(message)
val decrypted = encryption2.decrypt(encrypted.first, encrypted.second)
Assert.assertEquals(message, decrypted)
}
@Test
fun testEncryptedStringFromDifferentIVs() {
val message = "the secret message"
val key = ByteArray(16)
secureRandom.nextBytes(key)
val secretKey: SecretKey = SecretKeySpec(key, "AES")
val encryption = EncryptionAES(secretKey)
val encryption2 = EncryptionAES(secretKey)
val encrypted = encryption.encrypt( message)
val encrypted2 = encryption2.encrypt(message)
Assert.assertFalse(encrypted == encrypted2)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment