I have an Encryption class which generates a key during encryption and store it in AndroidKeyStore but when I try to retrieve the key it is null. NT: Im calling the encrypt function and decrypt function in 2 separate class and storing the encrypted info a a sharedperefrence. A PIN that user enter is being encrypted and stored, then when they open the application and enter a PIN the stored PIN is decrypted and compared.
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Base64
import android.util.Log
import java.security.KeyStore
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
class Encryption() {
// Alias for the key in the Keystore
private val keyAlias = "yourKeyAlias"
// Generate a key in the Keystore
private fun generateKey() {
try {
val keyGenerator =
KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
val keyGenParameterSpec = KeyGenParameterSpec.Builder(
keyAlias,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setKeySize(256) // Adjust the key size based on your requirements
.build()
keyGenerator.init(keyGenParameterSpec)
keyGenerator.generateKey()
Log.d("Encryption", "Key generation successful")
} catch (e: Exception) {
Log.e("Encryption", "Key generation failed: ${e.message}")
}
}
private fun getKey(): SecretKeySpec {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
if (!keyStore.containsAlias(keyAlias)) {
// If the key entry doesn't exist, generate the key
Log.d("Encryption", "Key dont exist ")
}
val secretKeyEntry = keyStore.getEntry(keyAlias, null) as KeyStore.SecretKeyEntry
val secretKey = secretKeyEntry.secretKey
// Log key information for debugging
Log.d("Encryption", "Key Algorithm: ${secretKey.algorithm}")
Log.d("Encryption", "Key Format: ${secretKey.format}")
Log.d("Encryption", "Encoded Key Material: ${secretKey.encoded?.contentToString()}")
// Ensure that the key material is non-null before creating SecretKeySpec
requireNotNull(secretKey.encoded) { "Key material is null" }
// Convert the SecretKey to SecretKeySpec
return SecretKeySpec(secretKey.encoded, "AES")
}
fun encrypt(strToEncrypt: String): Pair<ByteArray, ByteArray> {
generateKey()
val key = getKey()
val plainText = strToEncrypt.toByteArray(Charsets.UTF_8)
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.ENCRYPT_MODE, key)
val iv = cipher.iv
val encryptedData = cipher.doFinal(plainText)
return iv to encryptedData
}
fun decrypt(dataToDecrypt: String, encryptedIv: String): String {
val key = getKey()
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
val iv = Base64.decode(encryptedIv, Base64.DEFAULT)
val ivParameterSpec = IvParameterSpec(iv)
cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec)
val decryptedData = cipher.doFinal(Base64.decode(dataToDecrypt, Base64.DEFAULT))
return String(decryptedData, Charsets.UTF_8)
}
}
This is the log I'm receiving with the error: D Key generation successful D Key Algorithm: AES D Key Format: null D Encoded Key Material: null
E FATAL EXCEPTION: main
Process: com.example.identitypal, PID: 8886
java.lang.IllegalArgumentException: Key material is null
at com.example.identitypal.sign_login.Encryption.getKey(Encryption.kt:59)
at com.example.identitypal.sign_login.Encryption.encrypt(Encryption.kt:67)
at com.example.identitypal.sign_login.AppPasswordSetup.savePassword(AppPasswordSetup.kt:71)
at com.example.identitypal.sign_login.AppPasswordSetup.onCreate$lambda$0(AppPasswordSetup.kt:56)
at com.example.identitypal.sign_login.AppPasswordSetup.$r8$lambda$u6wUwfaPzmksbpwedB19ZIAllp8(Unknown Source:0)
at com.example.identitypal.sign_login.AppPasswordSetup$$ExternalSyntheticLambda0.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:7792)
at android.widget.TextView.performClick(TextView.java:16112)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1213)
at android.view.View.performClickInternal(View.java:7769)
at android.view.View.access$3800(View.java:910)
at android.view.View$PerformClick.run(View.java:30218)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8751)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
The purpose of Android KeyStore is to generate a pair of keys and store them safely, without exposing the PrivateKey or SecretKey.
This means that you will never be able to extract the bytes (secretKeyEntry.secretKey) of the SecretKey as it will always return null.
So instead of trying to build a
SecretKeySpecfrom the secretkey, the correct thing to do is to just use thesecretKeyEntry.secretKeyfor your encrypt and decrypt functions.