My teammate wrote kotlin code to encrypt zip files from android side using AES BC and I'm trying to decrypt it. But it's not happening. I need to write client side python code to decrypt those files
This is the kotlin code
import android.util.Base64
import android.util.Log
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
object AESEncryption {
fun generateSecretKey(): SecretKey {
val secureRandom = SecureRandom()
val keyGenerator = KeyGenerator.getInstance("AES")
//generate a key with secure random
keyGenerator?.init(256, secureRandom)
val key = keyGenerator.generateKey()
val secretKeyBase64 = Base64.encodeToString(key.encoded, Base64.NO_WRAP)
Log.e("AES KEY", "Generated AES key: $secretKeyBase64")
return key
}
fun readFile(filePath: String): ByteArray {
val file = File(filePath)
val fileContents = file.readBytes()
val inputBuffer = BufferedInputStream(
FileInputStream(file)
)
inputBuffer.read(fileContents)
inputBuffer.close()
return fileContents
}
fun saveFile(fileData: ByteArray, path: String) {
val file = File(path)
val bos = BufferedOutputStream(FileOutputStream(file, false))
bos.write(fileData)
bos.flush()
bos.close()
}
fun encrypt(yourKey: SecretKey, fileData: ByteArray): ByteArray {
val data = yourKey.getEncoded()
val skeySpec = SecretKeySpec(data, 0, data.size, "AES")
val cipher = Cipher.getInstance("AES", "BC")
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, IvParameterSpec(ByteArray(cipher.getBlockSize())))
return cipher.doFinal(fileData)
}
fun encryptDownloadedFile(filePath: String, secretKey: SecretKey) {
try {
val fileData = readFile(filePath)
//get secret key
//encrypt file
val encodedData = encrypt(secretKey, fileData)
saveFile(encodedData, "$filePath")
} catch (e: Exception) {
e.message?.let { Log.d("AEC", it) }
}
}
fun getSecretKey(): SecretKey{
val secretKeyEncoded = "key is here"
val decodedKey = Base64.decode(secretKeyEncoded, Base64.NO_WRAP)
val secretKey = SecretKeySpec(decodedKey, 0, decodedKey.size, "AES")
return secretKey
}
fun decrypt(yourKey: SecretKey, fileData: ByteArray): ByteArray {
val decrypted: ByteArray
val cipher = Cipher.getInstance("AES", "BC")
cipher.init(Cipher.DECRYPT_MODE, yourKey, IvParameterSpec(ByteArray(cipher.blockSize)))
decrypted = cipher.doFinal(fileData)
return decrypted
}
fun decryptEncryptedFile(filePath: String) {
val fileData = readFile(filePath)
val secretKey = getSecretKey()
val decodedData = decrypt(secretKey, fileData)
saveFile(decodedData, "$filePath")
}
}
and this is my python code
from Crypto.Cipher import AES
import base64
def get_secret_key():
# This is the same key used in the Android code
secret_key_encoded = "7same key"
decoded_key = base64.b64decode(secret_key_encoded)
return decoded_key
def decrypt_file(input_file_path, output_file_path=None):
try:
# If no output path specified, create one by removing .encrypted extension if present
if output_file_path is None:
output_file_path = input_file_path.replace('.encrypted', '')
if output_file_path == input_file_path:
output_file_path = input_file_path.replace('.zip', '_decrypted.zip')
# Read the encrypted file
with open(input_file_path, 'rb') as file:
encrypted_data = file.read()
# Get the secret key
key = get_secret_key()
# Create cipher object and decrypt the data
cipher = AES.new(key, AES.MODE_CBC, iv=bytes(16)) # 16 bytes of zeros as IV
decrypted_data = cipher.decrypt(encrypted_data)
padding_length = decrypted_data[-1]
decrypted_data = decrypted_data[:-padding_length]
# Write the decrypted data to output file
with open(output_file_path, 'wb') as file:
file.write(decrypted_data)
print(f"File decrypted successfully: {output_file_path}")
return True
except Exception as e:
print(f"Error during decryption: {str(e)}")
return False
# Example usage
if __name__ == "__main__":
# Example usage
encrypted_file_path = "com.android.settings_1736318346774.zip"
decrypt_file(encrypted_file_path)
with this code the zip is generated but not able to unzip it
In the Kotlin code, the specification of mode and padding is missing:
val cipher = Cipher.getInstance("AES", "BC")
For this reason, provider-dependent default values are used. On my machine (Android 15 / API level 35), e.g. the (insecure) ECB mode and PKCS#7 padding are applied.
Since you use the CBC mode in the Python code, the codes are incompatible and decryption fails. As a fix, explicitly specify the mode and padding in the Kotlin code:
val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC").
In principle, you could also use the ECB mode on the Python side to make both codes compatible. However, this is not advisable, as the ECB mode is insecure!
In addition, there is no need for manual unpadding in the Python code. PyCryptodome supports various paddings including PKCS#7, see Crypto.Util.Padding
.
Also note that the use of a static IV is a vulnerability. Instead, a random IV should be generated for each encryption. The IV, which is not a secret, is passed to the decrypting side together with the ciphertext, usually concatenated.