Rougher
Rougher

Reputation: 852

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":"[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

Upvotes: 1

Views: 137

Answers (2)

Topaco
Topaco

Reputation: 49131

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.

Upvotes: 2

Hassaan
Hassaan

Reputation: 7662

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";

Upvotes: 0

Related Questions