I am trying to use android keystore for encrypting and decrypting data and I am able to do it for single operation, however if I want to encrypt multiple keys and decrypt them, I am getting javax.crypto.BadPaddingException for all the keys except for the last one. only the last is getting successfully decrypted.
Please help me find the issue
class MainActivity {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_portfolio)
saveDataInKeyStore()
saveDataInKeyStore2()
getDataFromKeyStore()
getDataFromKeyStore2()
}
private fun saveDataInKeyStore() {
val secureText = "testfirststringusername"
keyStoreManager.saveData("username", secureText.toByteArray(),
successAction = {
printData(it.toString())
},
failedAction = {
printData("failed")
})
}
private fun getDataFromKeyStore() {
keyStoreManager.getData("username",
successAction = {
printData(String(it))
},
failedAction = {
printData("failed")
})
}
private fun saveDataInKeyStore2() {
val secureText = "testfirststringpassword"
keyStoreManager.saveData("password", secureText.toByteArray(),
successAction = {
printData(it.toString())
},
failedAction = {
printData("failed")
})
}
private fun getDataFromKeyStore2() {
keyStoreManager.getData("password",
successAction = {
printData(String(it))
},
failedAction = {
printData("failed")
})
}
private fun printData(msg: String) {
println("testing keystore status: " + msg)
}
}
class KeyStoreManager(val sharedPreferencesManager: SharedPreferencesManager) {
private val keyStore: KeyStore = KeyStore.getInstance(KEYSTORE).apply { load(null) }
private fun getKey(): Key? = keyStore.getKey(KEY_NAME, null)
fun saveData(dataKey: String, dataValue: ByteArray, successAction: (ByteArray) -> Unit, failedAction: () -> Unit) {
try {
val secretKey = createKey()
val cipher = getEncryptCipher(secretKey)
handleEncrypt(cipher, dataKey, dataValue, successAction)
} catch (e: Exception) {
failedAction()
}
}
private fun createKey(): Key {
val keyGenerator = KeyGenerator.getInstance(ALGORITHM, KEYSTORE)
val keyGenParameterSpec =
KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(BLOCK_MODE)
.setEncryptionPaddings(PADDING)
.build()
keyGenerator.init(keyGenParameterSpec)
return keyGenerator.generateKey()
}
private fun getEncryptCipher(key: Key): Cipher =
Cipher.getInstance(keyTransformation()).apply { init(Cipher.ENCRYPT_MODE, key) }
private fun handleEncrypt(
cipher: Cipher,
dataKey: String,
dataValue: ByteArray,
successAction: (ByteArray) -> Unit
) {
val iv = cipher.iv
val encryptedData = cipher.doFinal(dataValue)
saveEncryptedData(dataKey, encryptedData, iv)
// println("testing saving encrypted data $dataKey cipher= $cipher iv= $iv")
successAction(encryptedData)
}
private fun saveEncryptedData(dataKey: String, dataEncrypted: ByteArray, initializationVector: ByteArray) {
sharedPreferencesManager.putString(dataKey, Base64.encodeToString(dataEncrypted, Base64.DEFAULT))
sharedPreferencesManager.putString(
INITIALIZATION_VECTOR + dataKey,
Base64.encodeToString(initializationVector, Base64.DEFAULT)
)
}
fun getData(dataKey: String, successAction: (ByteArray) -> Unit, failedAction: () -> Unit) {
try {
val secretKey = getKey()
val initializationVector = getInitializationVector(dataKey)
if (secretKey != null && initializationVector != null) {
val cipher = getDecryptCipher(secretKey, initializationVector)
// println("testing getting decrypted data $dataKey cipher= $cipher iv= $initializationVector")
handleDecrypt(dataKey, cipher, successAction)
} else {
failedAction()
}
} catch (e: Exception) {
println("testing getting data from keystore manager failed: " + e)
failedAction()
}
}
private fun getInitializationVector(dataKey: String): ByteArray? {
val iv = sharedPreferencesManager.getString(INITIALIZATION_VECTOR + dataKey, "")
return when {
!"".equals(iv) -> Base64.decode(iv, Base64.DEFAULT)
else -> null
}
}
private fun getDecryptCipher(key: Key, iv: ByteArray): Cipher =
Cipher.getInstance(keyTransformation()).apply { init(Cipher.DECRYPT_MODE, key, IvParameterSpec(iv)) }
private fun handleDecrypt(dataKey: String, cipher: Cipher, successAction: (ByteArray) -> Unit) {
val encrypted = getEncryptedData(dataKey)
val data = cipher.doFinal(encrypted)
successAction(data)
}
private fun getEncryptedData(dataKey: String): ByteArray? {
val encryptedData = sharedPreferencesManager.getString(dataKey, "")
return when {
!"".equals(encryptedData) -> Base64.decode(encryptedData, Base64.DEFAULT)
else -> null
}
}
private fun keyTransformation() = listOf(ALGORITHM, BLOCK_MODE, PADDING).joinToString(separator = "/")
}
output I am getting
I/System.out: testing keystore status: [B#283eda1
I/System.out: testing keystore status: [B#8e7095
I/System.out: testing getting data from keystore manager failed: javax.crypto.BadPaddingException
I/System.out: testing keystore status: failed
I/System.out: testing keystore status: testfirststringpassword
Expected output
I/System.out: testing keystore status: [B#283eda1
I/System.out: testing keystore status: [B#8e7095
I/System.out: testing getting data from keystore manager failed: javax.crypto.BadPaddingException
I/System.out: testing keystore status: testfirststringusername
I/System.out: testing keystore status: testfirststringpassword
Related
i want to get value hashed in kotlin with private key RSA, but i have error 'android.security.KeyStoreException: Incompatible digest'.
this my code
fun generatePairKey() {
val start: Calendar = GregorianCalendar()
val end: Calendar = GregorianCalendar()
end.add(Calendar.YEAR, 1)
val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore"
)
kpg.initialize(
KeyGenParameterSpec.Builder(
KopraMobile().BIOMETRIC_ALIAS_KEY_PAIR,
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).setKeySize(2048)
.setUserAuthenticationRequired(false)
.setDigests(
KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA1
)
.setCertificateNotBefore(start.time)
.setCertificateNotAfter(end.time)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.build()
)
kpg.generateKeyPair()
}
fun getPrivateyPair(): PrivateKey {
val keyStore: KeyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val entry: KeyStore.Entry =
keyStore.getEntry(KopraMobile().BIOMETRIC_ALIAS_KEY_PAIR, null)
return (entry as KeyStore.PrivateKeyEntry).privateKey
}
fun getPublicKeyPair(): PublicKey {
val keyStore: KeyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
return keyStore.getCertificate(KopraMobile().BIOMETRIC_ALIAS_KEY_PAIR).getPublicKey()
}
fun getValAuthorization(_context: Context): String {
var encoded = ""
try {
val randomString = getRandomString(10)
val time = System.currentTimeMillis()
val random = (randomString + "" + time)
val rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
rsaCipher.init(Cipher.ENCRYPT_MODE, getPrivateyPair())
val byteArray = random.toByteArray(Charsets.UTF_8)
val cipherText = rsaCipher.doFinal(byteArray)
val randomHash = Base64.encodeToString(cipherText, Base64.NO_WRAP)
encoded = "Basic " + Base64.encodeToString(
("$random:$randomHash").toByteArray(),
Base64.NO_WRAP
)
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
return encoded
}
fun getRandomString(length: Int): String {
val charset = "ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz0123456789"
return (1..length)
.map { charset.random() }
.joinToString("")
}
i get error
android.security.KeyStoreException: Incompatible digest
when try to call getValAuthorization() on the part
rsaCipher.init(Cipher.ENCRYPT_MODE, getPrivateyPair())
how to solved it? thanks
I have an EncryptionManager class that is used to encrypt and decrypt data using the Android KeyStore. The implementation works fine for 99% of users, but for some of them we are seeing occasional obscure crashes such as
Fatal Exception: java.security.InvalidKeyException: Keystore operation failed
at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1693)
at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1750)
at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54)
at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109)
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)
at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
at javax.crypto.Cipher.init(Cipher.java:1143)
at javax.crypto.Cipher.init(Cipher.java:1084)
at com.app.encryption.implementation.EncryptionManager.encryptData(EncryptionManager.kt:39)
Caused by android.security.KeyStoreException: Key not found
at android.security.KeyStore.getKeyStoreException(KeyStore.java:1555)
at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1750)
at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54)
at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109)
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2984)
at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)
at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
at javax.crypto.Cipher.init(Cipher.java:1143)
at javax.crypto.Cipher.init(Cipher.java:1084)
at com.app.encryption.implementation.EncryptionManager.encryptData(EncryptionManager.kt:39)
and
Fatal Exception: javax.crypto.BadPaddingException
at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:609)
at javax.crypto.Cipher.doFinal(Cipher.java:2056)
at com.app.encryption.implementation.EncryptionManager.decryptData(EncryptionManager.kt:68)
Caused by android.security.KeyStoreException: Invalid argument
at android.security.KeyStore2.getKeyStoreException(KeyStore2.java:356)
at android.security.KeyStoreOperation.handleExceptions(KeyStoreOperation.java:78)
at android.security.KeyStoreOperation.finish(KeyStoreOperation.java:127)
at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer$MainDataStream.finish(KeyStoreCryptoOperationChunkedStreamer.java:228)
at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:181)
at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:603)
at javax.crypto.Cipher.doFinal(Cipher.java:2056)
at com.app.encryption.implementation.EncryptionManager.decryptData(EncryptionManager.kt:68)
It also seems to happen only on less common devices like the Motorola Edge Plus, the Moto G Pure, the Nokia X100, and other misc devices. I haven't seen these crashes happen on a Pixel or any other widely popular device.
My EncryptionManager class looks like this:
class EncryptionManager {
private val cipher by lazy {
Cipher.getInstance(TRANSFORMATION_PATTERN)
}
private val charset by lazy {
charset(CHARSET_NAME)
}
private val keyStore by lazy {
KeyStore.getInstance(KEY_STORE_TYPE).apply {
load(null)
}
}
private val keyGenerator by lazy {
KeyGenerator.getInstance(KEY_ALGORITHM_AES, KEY_STORE_TYPE)
}
fun encryptData(keyAlias: String, data: String): String {
return cipher.run {
init(Cipher.ENCRYPT_MODE, generateSecretKey(keyAlias))
val ivString = Base64.encodeToString(iv, Base64.DEFAULT)
var stringToEncrypt = ivString + IV_SEPARATOR
val bytes = doFinal(data.toByteArray(charset))
stringToEncrypt += Base64.encodeToString(bytes, Base64.DEFAULT)
stringToEncrypt
}
}
fun decryptData(keyAlias: String, encryptedData: String): String {
return cipher.run {
val split = encryptedData.split(IV_SEPARATOR.toRegex())
if (split.size != 2) {
throw IllegalArgumentException(IV_ERROR_MESSAGE)
}
val ivString = split[0]
val encryptedString = split[1]
val ivSpec = IvParameterSpec(Base64.decode(ivString, Base64.DEFAULT))
init(
Cipher.DECRYPT_MODE,
getSecretKey(keyAlias),
ivSpec,
)
val decryptedData = doFinal(Base64.decode(encryptedString, Base64.DEFAULT))
String(decryptedData)
}
}
fun deleteAllData() {
val aliases = keyStore.aliases().iterator()
aliases.forEach {
try {
keyStore.deleteEntry(it)
} catch (e: Exception) {
Timber.e(e)
}
}
}
private fun generateSecretKey(keyAlias: String): SecretKey {
return keyGenerator.apply {
init(
KeyGenParameterSpec
.Builder(keyAlias, PURPOSE_ENCRYPT or PURPOSE_DECRYPT)
.setBlockModes(BLOCK_MODE_CBC)
.setEncryptionPaddings(ENCRYPTION_PADDING_PKCS7)
.build(),
)
}.generateKey()
}
private fun getSecretKey(keyAlias: String): SecretKey {
return (keyStore.getEntry(keyAlias, null) as KeyStore.SecretKeyEntry).secretKey
}
companion object {
private const val TRANSFORMATION_PATTERN = "AES/CBC/PKCS7Padding"
private const val KEY_STORE_TYPE = "AndroidKeyStore"
private const val CHARSET_NAME = "UTF-8"
private const val IV_SEPARATOR = "]"
private const val IV_ERROR_MESSAGE = "Passed data is incorrect. There was no IV specified with it."
}
}
I've spent the last week searching other KeyStore related issues, but I haven't been able to find anything that might explain why the crashes are only happening to a small subset of users. Is there something wrong with my EncryptionManager implementation? Something else I'm missing?
I am trying to store encrypted data in a SQL database using AES with Initialization Vector (IV). I am able to do this with the following class:
class Encrypted (wordE : String) {
val keyGenerator: KeyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,"AndroidKeyStore")
val keyGenParameterSpec = KeyGenParameterSpec.Builder("MyKeyAlias",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build()
fun genKey(){
keyGenerator.init(keyGenParameterSpec)
keyGenerator.generateKey()
}
fun getKey(): SecretKey {
genKey()
val keystore = KeyStore.getInstance("AndroidKeyStore")
keystore.load(null)
val secretKeyEntry = keystore.getEntry("MyKeyAlias", null) as KeyStore.SecretKeyEntry
return secretKeyEntry.secretKey
}
fun encryptData(data: String): Pair<ByteArray, ByteArray> {
val cipher = Cipher.getInstance("AES/CBC/NoPadding")
var temp = data
while (temp.toByteArray(Charsets.UTF_8).size % 16 != 0)
temp += "\u0020"
cipher.init(Cipher.ENCRYPT_MODE, getKey())
val ivBytes = cipher.iv
val encryptedBytes = cipher.doFinal(temp.toByteArray(Charsets.UTF_8))
return Pair(ivBytes, encryptedBytes)
}
val pair = encryptData(wordE)
val encrypted = pair.second.toString(Charsets.UTF_8)
val iv = pair.first
}
but I have some problems to decrypt the data from the database.
For decryption I have implemented another class:
class Decrypted (dataD: String, ivD :String) {
fun getKey(): SecretKey {
val keystore = KeyStore.getInstance("AndroidKeyStore")
keystore.load(null)
val secretKeyEntry = keystore.getEntry("MyKeyAlias", null) as KeyStore.SecretKeyEntry
return secretKeyEntry.secretKey
}
fun decryptData(data: String, iv : String) : String {
val spec = IvParameterSpec(iv.toByteArray())
val decipher = Cipher.getInstance("AES/CBC/NoPadding")
decipher.init(Cipher.DECRYPT_MODE, getKey(), spec)
val encryptedData: ByteArray = data.toByteArray()
return decipher.doFinal(encryptedData).toString().trim()
}
val decryptedData = decryptData(dataD, ivD)
}
I understood that IV must be the same as the one generated during encryption, so I also stored IV in the database as .
From my logcat I get an "InvocationTargetException" to "Invalid IV" message.
I can see that when I call the Decrypted class enter for decryption:
dataEncrypted: /�,#�j3�RqLrY�
iv_stored: [B#5a36422
I am developing an android app.
I want to encrypt/decrypt some sensitive data (jwt token) into SharedPreference.
So I wrote the below code.
fun initKeyStore() {
val alias = "${packageName}.rsakeypairs"
val keyStore = KeyStore.getInstance("AndroidKeyStore").apply {
load(null)
}
if (keyStore.containsAlias(alias)) {
} else {
SLog.d(LogTag.SECURE, "[cipher] No keypair for $alias, creating a new one")
with(KeyPairGenerator.getInstance(KEY_ALGORITHM_RSA, "AndroidKeyStore"), {
val spec = KeyGenParameterSpec.Builder(alias,
PURPOSE_ENCRYPT or PURPOSE_DECRYPT)
.setAlgorithmParameterSpec(RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4))
.setBlockModes(BLOCK_MODE_CBC)
.setEncryptionPaddings(ENCRYPTION_PADDING_RSA_PKCS1)
.setDigests(DIGEST_SHA512, DIGEST_SHA384, DIGEST_SHA256)
.setUserAuthenticationRequired(false)
.build()
initialize(spec)
generateKeyPair()
})
}
keyEntry = keyStore.getEntry(alias, null)
}
fun String.encrypt(): String? {
cipher.init(Cipher.ENCRYPT_MODE, (keyEntry as KeyStore.PrivateKeyEntry).certificate.publicKey)
val bytes = this.toByteArray(Charsets.UTF_8)
val encryptedBytes = cipher.doFinal(bytes)
val base64EncryptedBytes = Base64.encode(encryptedBytes, Base64.DEFAULT)
return String(base64EncryptedBytes)
}
fun String.decrypt(): String {
cipher.init(Cipher.DECRYPT_MODE, (keyEntry as KeyStore.PrivateKeyEntry).privateKey)
val base64EncryptedBytes = this.toByteArray(Charsets.UTF_8)
val encryptedBytes = Base64.decode(base64EncryptedBytes, Base64.DEFAULT)
val decryptedBytes = cipher.doFinal(encryptedBytes)
return String(decryptedBytes)
}
But when the app tries to decrypt the encrypted data, Exception occurred.
javax.crypto.IllegalBlockSizeException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:513)
at javax.crypto.Cipher.doFinal(Cipher.java:2055)
...
Caused by: android.security.KeyStoreException: Invalid input length
at android.security.KeyStore.getKeyStoreException(KeyStore.java:1539)
at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:132)
The length of the JWT token that I am using is very long. (More than 800)
If I try to encrypt/decrypt short text, it works fine...
How can I encrypt/decrypt the long text?
In order to encrypt a long text, you either increase the key size (which is probably a bad idea, since it will take much more time to generate this key), or you split the text into chunks, encrypt those chunks one by one, and save them as a string array.
The maximum limit of Assymmetric Encryption is 245 character.
it can be fixed with the chunks of the Long String
object SecurePreferencesHelper {
private const val chunkSize = 240
private fun getNumberOfChunksKey(key: String) = "${key}_numberOfChunks"
fun setLongStringValue(key: String, value: String) {
val chunks = value.chunked(chunkSize)
SecurePreferences.setValue(getNumberOfChunksKey(key), chunks.size)
chunks.forEachIndexed { index, chunk ->
SecurePreferences.setValue("$key$index", chunk)
}
}
fun getLongStringValue(key: String): String? {
val numberOfChunks = SecurePreferences.getIntValue(getNumberOfChunksKey(key), 0)
if (numberOfChunks == 0) {
return null
}
return (0 until numberOfChunks)
.map { index ->
val string = SecurePreferences.getStringValue("$key$index", null) ?: run {
return null
}
string
}.reduce { accumulator, chunk -> accumulator + chunk }
}
fun removeLongStringValue(key: String) {
val numberOfChunks = SecurePreferences.getIntValue(getNumberOfChunksKey(key), 0)
(0 until numberOfChunks).map { SecurePreferences.removeValue("$key$it") }
SecurePreferences.removeValue(getNumberOfChunksKey(key))
}
fun containsLongStringValue(key: String): Boolean {
return SecurePreferences.contains(getNumberOfChunksKey(key))
}
}
For reference pls refer link
click here
I'm developing an app that shows sensible information so I have to
encrypt all informations stored on Room Database.
After a lot of research, I choosed to encypt using AES, generating a random key
and storing on KeyStore (minSdk: 24 btw).
private fun encript(plain: String): String? {
return try {
generateKey()
encData(plain)
} catch (e: Throwable) {
if (!BuildConfig.DEBUG){
Crashlytics.logException(e)
}
null
}
}
private fun encData(plain: String): String? {
val sKey = getSecretKey()
iv = ByteArray(12)
secRng = SecureRandom()
secRng.nextBytes(iv)
val cipher = Cipher.getInstance(AES_MODE)
val parameterSpec = GCMParameterSpec(128, iv)
cipher.init(Cipher.ENCRYPT_MODE, sKey, parameterSpec)
cipText = cipher.doFinal(plain.toByteArray())
return encBuffer()
}
private fun encBuffer(): String? {
val byteBuffer = ByteBuffer.allocate(4 + iv.size + cipText.size)
byteBuffer.putInt(iv.size)
byteBuffer.put(iv)
byteBuffer.put(cipText)
val cipherMessage = byteBuffer.array()
//clean()
return Base64.encodeToString(cipherMessage, Base64.DEFAULT)
}
So i must show all these informations on a list, so Im decrypting all information
on viewholder. the problem is that is too slow when it shows a lot of items,
so i decided to try an async descrypt inside the viewholder and for my surprise
i got a lot of "Unitialized keystore" exception, letting y data encrypted,
it's weird because when i scroll down and up, some viewholders decrypt successfull
and others not, it's pretty random, an it seems that viewholder try to decrypt more
than once. PS: I can't cache decrypted data on SharedPrerences for security reasons
fun decription(encStr: String): String? {
return try {
dec(encStr)
} catch (e: Throwable) {
Log.d("cripto", "Here, Trowing Unitialized Keystore")
if (!BuildConfig.DEBUG){
Crashlytics.logException(e)
}
null
}
}
private fun dec(encStr: String): String {
val byteBuffer = ByteBuffer.wrap(Base64.decode(encStr, Base64.DEFAULT))
val ivLength = byteBuffer.int
if (ivLength < 12 || ivLength >= 16) { // check input parameter
throw IllegalArgumentException("invalid iv length")
}
val iv = ByteArray(ivLength)
byteBuffer.get(iv)
val cipherText = ByteArray(byteBuffer.remaining())
byteBuffer.get(cipherText)
return callCip(cipherText, iv)
}
private fun callCip(cipText: ByteArray, iv: ByteArray): String {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), GCMParameterSpec(128, iv))
val plainText = cipher.doFinal(cipText)
return String(plainText)
}
And my viewholder code:
doAsync {
var name = ""
var lname = ""
var patRecord = ""
if (value?.patient != null){
name = PatSecHelper.nToPat(value.patient?.name?.trim() ?: "")
lname = PatSecHelper.nToPat(value.patient?.lastName?.trim() ?: "")
patRecord = PatSecHelper.nToPat(value.patient?.patientRecord?.trim() ?: "")
}
onComplete {
if (value?.patient == null){
view.textViewPatientId.visibility = View.GONE
}else{
if (name == "" || lname == "") {
view.textViewPatient.text = "Error..."
}else{
view.textViewPatient.text = "$name $lname"
}
if (patRecord == ""){
view.textViewPatientId.text = "Error..."
}else{
view.textViewPatientId.text = patRecord
}
}
}
}
**EDIT:
Here is the code that i'm using to genarete and get keys
private fun generateKey(){
keyStore = KeyStore.getInstance(AndroidKeyStore)
keyStore.load(null)
if (!keyStore.containsAlias(KEY_ALIAS)) {
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, AndroidKeyStore)
keyGenerator.init(
KeyGenParameterSpec.Builder(KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build())
keyGenerator.generateKey()
}
}
#Throws(Exception::class)
private fun getSecretKey(): java.security.Key {
keyStore = KeyStore.getInstance(AndroidKeyStore)
keyStore.load(null)
return keyStore.getKey(KEY_ALIAS, null)
}
Here are the problems I see:
I am not sure what the implementation is of generateKey() but if it does what it says on the tin then don't generate your key every time you encrypt. As I believe you are doing here:
private fun encript(plain: String): String? {
return try {
generateKey() // <- why are you doing this every time? are you storing a unique key with everything you encrypt?
encData(plain)
} catch (e: Throwable) {
if (!BuildConfig.DEBUG) {
Crashlytics.logException(e)
}
null
}
}
This will mean you have a new key every time you encrypt something? Rather, this should be initialized and ready before you try to encrypt/decrypt.
And, if this is the only place you do generate your key, then how do you know that when you call this:
private fun callCip(cipText: ByteArray, iv: ByteArray): String {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), GCMParameterSpec(128, iv)) // <- Are you sure you have called encrypt before callin this?
val plainText = cipher.doFinal(cipText)
return String(plainText)
}
That you have a key generated to be fetched? Pulling out generateKey() and making sure you have called it before you do any encrypt/decrypt actions should fix your problem I think.