2

I am trying to encrypt a UTF-8 string on Python with AES and decrypt it in Kotlin, here is the Python encrypt part:

class aes():

    def __init__(self, key):
        self.bs = AES.block_size
        self.key = hashlib.sha256(key.encode()).digest()

    def encrypt(self, raw):
        raw = self.pad(raw)
        iv = self.key[:self.bs]  # Random.new().read(self.bs)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw.encode()))

    def pad(self, s):
        return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)

...

client_key = self.rsa.decrypt(int(connection.recv(bufsize).decode()))

enc = aes(client_key)
em = enc.encrypt("AES_OK")

connection.send(em+b'\0')

And the Kotlin part decrypting it:

object AES256 {
    private val decorder = Base64.getDecoder()
    private fun cipher(opmode:Int, secretKey:String):Cipher {

        val c = Cipher.getInstance("AES/CBC/PKCS7Padding")

        val sk = SecretKeySpec(secretKey.toByteArray(Charsets.UTF_8), "AES")
        val iv = IvParameterSpec(secretKey.substring(0, 16).toByteArray(Charsets.UTF_8))
        c.init(opmode, sk, iv)
        return c
    }
    @RequiresApi(Build.VERSION_CODES.O)
    fun decrypt(str:String, secretKey:String):String {
        val byteStr = decorder.decode(str.toByteArray(Charsets.UTF_8))
        // Error here
        return String(cipher(Cipher.DECRYPT_MODE, secretKey).doFinal(byteStr))
    }
}

fun readBytes(input: BufferedReader) : String {
    var byte: Int = input.read()
    var r = ""
    while(byte != 0) {
        r += byte.toChar()
        byte = input.read()
    }
    return r
}

resposta = readBytes(input)
resposta = AES256.decrypt(resposta, atc_aesk.toString(Charsets.UTF_8))

And I get the following exception:

javax.crypto.BadPaddingException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT
  1. All the RSA and connection code works properly, the ciphertext and key are the same on both sides.
  2. I'm using a 32 bytes key, I also tried a 16 bytes one, same error.

I would greatly appreciate any help or input, thanks.

President James K. Polk
  • 40,516
  • 21
  • 95
  • 125
Facundo
  • 31
  • 5
  • I noticed at least the following mistakes: 1) you derive the key differently in each language, and 2) you prepend the IV to cipher in python, but you decrypt it in Kotlin. This should not generate an exception though. – President James K. Polk Jun 08 '20 at 12:34
  • The key used in both is the same one. I also want to add that with no padding, the decrypted plaintext is gibberish. – Facundo Jun 09 '20 at 09:44
  • How can it be the same? You hash the string with sha256 in the python code but do nothing at all with in the kotlin code. It's practically guaranteed to be different! – President James K. Polk Jun 09 '20 at 13:31
  • I am so dumb, thank you for your time and sorry for my dumb mistake. – Facundo Jun 10 '20 at 07:03

2 Answers2

1

The user President James K. Polk answered this in a comment, the mistake was that I hashed the key only in the Python code and not in Kotlin... self.key = hashlib.sha256(key.encode()).digest()

I tried finding the problem like a whole week, I really feel so dumb, thank you President James K. Polk.

Facundo
  • 31
  • 5
0

I had exactly the same use case and seemed to have copied the same code from some places online. Still took me a second. Here is a working pair:

class AESCipher(object):

    def __init__(self, key):
        self.bs = AES.block_size
        self.key = hashlib.sha256(key.encode()).digest()

    def encrypt(self, raw):
        raw = self._pad(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw.encode())).decode('utf-8')

    def _pad(self, s):
        return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)

class AESCipher(private val key: String) {

    private fun createCipher(mode: Int, ivBytes: ByteArray): Cipher {
        val c = Cipher.getInstance("AES/CBC/PKCS7Padding")
        val sk = SecretKeySpec(key.getSha256(), "AES")
        val iv = IvParameterSpec(ivBytes)
        c.init(Cipher.DECRYPT_MODE, sk, iv)
        return c
    }

    fun decrypt(data: String): ByteArray {
        val bytes = Base64.decode(data, Base64.DEFAULT)
        val ivBytes = bytes.take(16).toByteArray()
        val rawDataBytes = bytes.drop(16).toByteArray()
        val cipher = createCipher(Cipher.DECRYPT_MODE, ivBytes)
        return cipher.doFinal(rawDataBytes)
    }

    private fun String.getSha256(): ByteArray {
        val digest = MessageDigest.getInstance("SHA-256").also { it.reset() }
        return digest.digest(this.toByteArray())
    }
}

If you pass the same key string to them, they will work together. Notice that the data exposed and consumed is already a Base64 string.

crysxd
  • 3,177
  • 20
  • 32