When Google refered me to this issue, I added to IvParameterSpec the first 16 bytes of hash of provided key. This ensures to me that for each encryption/decryption the same IV is used:
fun encipher(value: String, key: String): String {
val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
cipher.init(
Cipher.ENCRYPT_MODE,
SecretKeySpec(hash(key, 32), "AES"),
IvParameterSpec(hash(key, 16))
)
return Base64.encodeToString(cipher.doFinal(value.toByteArray()), Base64.DEFAULT)
}
fun decipher(value: String?, key: String = CRYPT_PASS): String {
val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
cipher.init(
Cipher.DECRYPT_MODE,
SecretKeySpec(hash(key, 32), "AES"),
IvParameterSpec(hash(key, 16))
)
return String(cipher.doFinal(Base64.decode(value?.toByteArray(), Base64.DEFAULT)))
}
The problem is, the decryption does not give the original text back. Let's say I encrypt this text
Arctium is a genus of biennial plants commonly known as burdock, family Asteraceae.
but, after decryption I get
w@[GL^FQAURQ[us of biennial plants commonly known as burdock, family Asteraceae.
Why does it happen? Did I do something wrong?
EDIT: Here is the missing part:
// This one is called while encrypting and decrypting
private fun hash(key: String?, length: Int): ByteArray {
return hash(hash(key).substring(0, length)).substring(
0,
length
).toByteArray()
}
private fun hash(data: String?): String {
val md = MessageDigest.getInstance("SHA-256")
data?.toByteArray()?.let { md.update(it) }
val result = StringBuffer()
for (bt in md.digest())
result.append(((bt and 0xFF.toByte()) + 0x100).toString(16).substring(1))
return result.toString()
}
Test data:
val newData = arrayListOf(
Data("First test string...", "FirstKey"),
Data("Second test string...", "SecondKey"),
Data("First test string...", "FirstKey"))
newData.forEach {
roomDb.addData(encipher(it.text, it.pass))
}
....
roomData.forEachIndexed { s, i ->
decData.add(decipher(s), newData[i]))
}