skip to Main Content

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":"[email protected]","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

2

Answers


  1. Errors:

    • IV Size Issue:
      The IV (Initialization Vector) must be exactly 16 bytes for AES-256-CBC. If the IV size is incorrect, the encryption/decryption process will fail.

    • Padding Issue:
      Using OPENSSL_ZERO_PADDING requires the data to be a multiple of the block size (16 bytes). If not, the function may fail or produce incorrect results. By default, openssl_encrypt and openssl_decrypt handle padding correctly without specifying it.

    Solution:

    function encrypt($data, $key) {
        $iv = substr($key, 0, 16); // Use a proper IV
        $encryptedData = openssl_encrypt($data, 'AES-256-CBC', $key, 0, $iv);
        return base64_encode($encryptedData);
    }
    
    function decrypt($encryptedData, $key) {
        $iv = substr($key, 0, 16); // Use the same IV as in encrypt
        $decryptedData = openssl_decrypt(base64_decode($encryptedData), 'AES-256-CBC', $key, 0, $iv);
        return $decryptedData;
    }
    

    Usage:

    $data = "Hello, World!";
    $key = "12345678901234567890123456789012"; // 32-byte key for AES-256
    
    $encrypted = encrypt($data, $key);
    echo "Encrypted: " . $encrypted . "n";
    
    $decrypted = decrypt($encrypted, $key);
    echo "Decrypted: " . $decrypted . "n";
    
    Login or Signup to reply.
  2. Running your Python code gives the following (Base64 encoded) ciphertext:

    r9Ufy4dnw2Yr0apRe1q0N0eiE+2VEOUbO35CeLQbJia1x8IM66nzweFdnJ32gpQTsZ33As6XjBZUU3bjpZrwyndeTP1ln8890qSyqlmlXOwiYnDHe5Zjg9Ws1F+zR0nl
    

    To decrypt this with the PHP code:

    • the default PKCS#7 padding must not be disabled (i.e. remove the OPENSSL_ZERO_PADDING option)
    • it must not be Base64 decoded twice (i.e. remove the explicit Base64 decoding or alternatively apply the OPENSSL_RAW_DATA option)

    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":"[email protected]","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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search