Home > front end >  Python AES-256-CBC Encrypt to PHP Decrypt with openssl_decrypt return false
Python AES-256-CBC Encrypt to PHP Decrypt with openssl_decrypt return false

Time:12-25

I am encrypting data successfully in python and sending the url safe data to PHP. When I try to decrypt the data with the openssl_decrypt PHP method it returns false. I have been reading for 2 days know searching for a solution with none of them solving my problem [there are 2 similar questions on this forum, but they did not help, Q1, Q2 ].

The encryption uses python3.8 and package PyCryptodome:

from Crypto.Cipher import AES
import base64
import hashlib
import os
import secrets

BLOCK = 16
PADDING = '~'
iv = secrets.token_urlsafe(16)
secretKey = secrets.token_urlsafe(32) # 32 bytes is used as AES 256 expects 256 bits
encoding = 'latin-1'
s = 'very secret data' # secret to be encrypted

k = hashlib.sha256(secretKey.encode(encoding)).hexdigest()[:32].encode(encoding) # key hashed and encoded
pad = s   (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
cipher = AES.new(key=k, mode=AES.MODE_CBC, IV=hashlib.sha256(iv.encode(encoding)).hexdigest()[:16].encode(encoding)) # cipher object

encrypted_secret = base64.urlsafe_b64encode(cipher.encrypt(pad.encode(encoding)))
print("Key: ", secretKey, "\n", "IV:",iv, "\n", "Encrypted:", encrypted_secret.decode(encoding))

Outputs[0]:

Key:  cdLTCHW95FjHF6ESu2Rkm-90AUbzDBv71HrdshsEx3k 
 IV: b16ezD5O05EDNovsqLExUg 
 Encrypted: RJqA3-n6d7hmKgj7biiwUKD0SjRjm__4f42P06R-qO8=

*I decode the encrypted secret result because it is sent as a json encoded object and json`s encoder complains about byte objects in its midst.

Decrypting in PHP is less code (for tests I copy key and iv over, but the iv is newly created every time and the secret key is stored on different server):

$key = 'cdLTCHW95FjHF6ESu2Rkm-90AUbzDBv71HrdshsEx3k';
$iv = 'b16ezD5O05EDNovsqLExUg';
$method = "AES-256-CBC";
$blocksize = 16;
$padwith = '~';
$decoded_secret = 'RJqA3-n6d7hmKgj7biiwUKD0SjRjm__4f42P06R-qO8='; // secret is sent as string

$hashKey = substr(hash('sha256', $key), 0, 32);
$iv_len = openssl_cipher_iv_length($method);
$iv_hash = substr(hash('sha256', $iv), 0, $iv_len); // cipher expects an IV of precisely 16 bytes, padding with \\0

$decrypted_secret = openssl_decrypt($decoded_secret, $method, $hashKey, OPENSSL_RAW_DATA, $iv_hash);

$what = rtrim($decrypted_secret, $padwith);
var_dump($decrypted_secret, $what);

Outputs[1]: bool(false) string(0) ""

I have tried setting utf-8 as encoding in python, decoding the encrypted string with base64 in php, changing block size to 32, not using hashlib on python side, but none of the changes gave the expected decrypted result.

Can someone please give me a clue to solve this problem?

CodePudding user response:

You actually need two clues, so let's call it a holiday bonus :-)

  • your data is in urlsafe-base64, but your php doesn't decode it, so it tries to decrypt entirely wrong data. php doesn't (AFAICS) directly support urlsafe, so you need to convert it; you can then decode it and pass to openssl_decrypt, or simply call openssl_decrypt without OPENSSL_RAW_DATA because it defaults to (traditional aka MIME or PEM or PGP or XML) base64.

  • your encryption doesn't use PKCS5/7 padding, but OpenSSL uses that by default, so your decryption fails. You need to use OPENSSL_ZERO_PADDING which becomes in this case a no-op and then (as you already did) do the unpad yourself.

With either the uncommented or commented line in this code, it works:

<?php
$key = 'cdLTCHW95FjHF6ESu2Rkm-90AUbzDBv71HrdshsEx3k';
$iv = 'b16ezD5O05EDNovsqLExUg';
$method = "AES-256-CBC";
$blocksize = 16; // not used
$padwith = '~';
$secret = 'RJqA3-n6d7hmKgj7biiwUKD0SjRjm__4f42P06R-qO8='; // secret is sent as string
$fixed_secret = str_replace(array('-','_'),array(' ','/'),$secret);
$decoded_secret = base64_decode($fixed_secret); // maybe used

$hashKey = substr(hash('sha256', $key), 0, 32);
$iv_len = openssl_cipher_iv_length($method);
$iv_hash = substr(hash('sha256', $iv), 0, $iv_len);

$decrypted_secret = openssl_decrypt($fixed_secret, $method, $hashKey, OPENSSL_ZERO_PADDING, $iv_hash);
//$decrypted_secret = openssl_decrypt($decoded_secret, $method, $hashKey, OPENSSL_RAW_DATA OPENSSL_ZERO_PADDING, $iv_hash);

$what = rtrim($decrypted_secret, $padwith);
var_dump($decrypted_secret, $what);
?>
->
string(32) "very secret data~~~~~~~~~~~~~~~~"
string(16) "very secret data"
  • Related