pythonphpencryptionaescbc-mode

Decrypt AES-256-CBC in php


I encrypt in Python this way:

from Crypto.Cipher import AES
from base64 import b64decode, b64encode

BLOCK_SIZE = 16
pad = lambda s: s + (BLOCK_SIZE - len(s.encode()) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s.encode()) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]


class AESCipher:
    def __init__(self, key: str, iv: str):
        self.key = key
        self.iv = iv

    def encrypt(self, text):
        text = pad(text).encode()
        cipher = AES.new(key=self.key.encode(), mode=AES.MODE_CBC, iv=self.iv.encode())
        encrypted_text = cipher.encrypt(text)
        enc = b64encode(encrypted_text).decode('utf-8')
        return enc

    def decrypt(self, encrypted_text):
        encrypted_text = b64decode(encrypted_text)
        cipher = AES.new(key=self.key.encode(), mode=AES.MODE_CBC, iv=self.iv.encode())
        decrypted_text = cipher.decrypt(encrypted_text)
        return unpad(decrypted_text).decode('utf-8')


key = '12345hg5bnlg4mtae678900cdy7ta4vy'
iv = '12345hg5bnlg4mtae678900cdy7ta4vy'[:16]
json = '{"email":"test@test.com","password":"secret","firstName":"test","lastName":"test"}'

# Encrypt
cipher = AESCipher(key, iv)

enc = cipher.encrypt(json)

print(enc)

And I need to decrypt in PHP this way:

function encrypt($data, $key) {
    $encryptedData = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_ZERO_PADDING, substr($key, 0, 16));
    return base64_encode($encryptedData);
}

function decrypt($encryptedData, $key) {
    $decryptedData = openssl_decrypt(base64_decode($encryptedData), 'AES-256-CBC', $key, OPENSSL_ZERO_PADDING, substr($key, 0, 16));
    return $decryptedData;
}

Unfortunately, I get the empty result on decryption. Could you tell where is my bug? I don't get any error, so it is difficult to understand where the problem is.

Thanks


Solution

  • Running your Python code gives the following (Base64 encoded) ciphertext:

    r9Ufy4dnw2Yr0apRe1q0N0eiE+2VEOUbO35CeLQbJia1x8IM66nzweFdnJ32gpQTsZ33As6XjBZUU3bjpZrwyndeTP1ln8890qSyqlmlXOwiYnDHe5Zjg9Ws1F+zR0nl
    

    To decrypt this with the PHP code:

    The following (fixed) PHP code successfully decrypts the ciphertext:

    $key = '12345hg5bnlg4mtae678900cdy7ta4vy';
    $ciphertext = 'r9Ufy4dnw2Yr0apRe1q0N0eiE+2VEOUbO35CeLQbJia1x8IM66nzweFdnJ32gpQTsZ33As6XjBZUU3bjpZrwyndeTP1ln8890qSyqlmlXOwiYnDHe5Zjg9Ws1F+zR0nl';
    
    function decrypt($encryptedData, $key) {
        $decryptedData = openssl_decrypt($encryptedData, 'AES-256-CBC', $key, 0, substr($key, 0, 16)); // 1. don't disable padding, 2. remove the explicit Base64 decoding
        return $decryptedData;                       
    }
    print(decrypt($ciphertext, $key)); // {"email":"test@test.com","password":"secret","firstName":"test","lastName":"test"}
    

    Since the reuse of key/IV pairs is a vulnerability, it is more secure to generate a random IV during encryption. The IV is not secret and is passed to the decrypting side together with the ciphertext (usually concatenated). Also note that PyCryptodome supports padding, see Crypto.Util.padding, so there is no need for a custom implementation.