How to Do Aes256 Decryption in PHP

How to do AES256 decryption in PHP?

I'm not terribly familiar with this stuff, but it seems like trying MCRYPT_RIJNDAEL_256 in place of MCRYPT_RIJNDAEL_128 would be an obvious next step...

Edit: You're right -- this isn't what you need. MCRYPT_RIJNDAEL_128 is in fact the right choice. According to the link you provided, your key and IV are twice as long as they should be:

// How do you do 256-bit AES encryption in PHP vs. 128-bit AES encryption???
// The answer is: Give it a key that's 32 bytes long as opposed to 16 bytes long.
// For example:
$key256 = '12345678901234561234567890123456';
$key128 = '1234567890123456';

// Here's our 128-bit IV which is used for both 256-bit and 128-bit keys.
$iv = '1234567890123456';

Encrypting and Decrypting AES 256 in both php and vb.net

AESEncryptStringToBase64() in the VB code encrypts a plaintext with AES in CBC mode with PKCS7 padding. The key size determines the AES variant, e.g. 32 bytes for AES-256. During encryption a GUID is generated which acts as IV. After encryption, the IV and ciphertext are concatenated and Base64 encoded.

A possible implementation in PHP is:

function encrypt($key, $plaintext){
$iv = random_bytes(16);
$ciphertext = openssl_encrypt($plaintext, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
$ivCiphertext = $iv . $ciphertext;
$ivCiphertextB64 = base64_encode($ivCiphertext);
return $ivCiphertextB64;
}

Note:

  • In the PHP code a cryptographically secure IV, see CSPRNG, is generated instead of a GUID. This is at least as secure as the GUID used in the VB code, see Is Microsoft's GUID generator cryptographically secure?.
  • In the PHP code AES-256 is specified and therefore a 32 bytes key is required. In the VB code, therefore, a 32 bytes key must also be used for compatibility. In case of a different key size in the VB code, the AES variant in the PHP code must be adapted accordingly, e.g. aes-128-cbc for a 16 bytes key.

AESDecryptBase64ToString() in the VB code is the counterpart and therefore uses the same algorithm, mode and padding. The encrypted data is first Base64 decoded. The first 16 bytes are the IV, the rest is the ciphertext which is then decrypted.

A possible implementation in PHP is:

function decrypt($key, $ivCiphertextB64){
$ivCiphertext = base64_decode($ivCiphertextB64);
$iv = substr($ivCiphertext, 0, 16);
$ciphertext = substr($ivCiphertext, 16);
$decryptedData = openssl_decrypt($ciphertext, "aes-256-cbc", $key, OPENSSL_RAW_DATA, $iv);
return $decryptedData;
}

Tests:

// Test 1:
$key = '00000000000000000000000000000000';
$plaintext = 'The quick brown fox jumps over the lazy dog';
$ivCiphertextB64 = encrypt($key, $plaintext);
$decrypted = decrypt($key, $ivCiphertextB64);
print("Test 1 - Ciphertext: " . $ivCiphertextB64 . PHP_EOL);
print("Test 1 - Decrypted: " . $decrypted . PHP_EOL);

// Test 2: Decrypt ciphertext from VB
$ivCiphertextB64 = '6z4IGlnv5UOd+MWUOfP5m4ymJ/XUp+VijU0D4xBLRr/T1JWAsxRApW6aJgptmN4q2f0seibD/2jktvkDydz33g==';
$decrypted = decrypt($key, $ivCiphertextB64);
print("Test 2 - Decrypted: " . $decrypted . PHP_EOL);

// Test 3: Encrypt plaintext for VB
$plaintext = 'The quick brown fox jumps over the lazy dog';
$ivCiphertextB64 = encrypt($key, $plaintext);
print("Test 3 - Encrypted: " . $ivCiphertextB64 . PHP_EOL);

with the following possible output:

Test 1 - Ciphertext: cadDJ82W94xKz4MQlvZ4IPBYvytpJezgbAY+ZYi76Z/zcGieUFDZLzEsjenolbEP2pR9JTHOHnG+ylJCXZd45g==
Test 1 - Decrypted: The quick brown fox jumps over the lazy dog
Test 2 - Decrypted: The quick brown fox jumps over the lazy dog
Test 3 - Encrypted: Teo0qUA553EJ9y9O0lb0gjVzWHE8+EQyklnbq6v8ziK5SXNAhX6HdyfdtHkyQUKAWXmFazWx2bEkIDwqjM+aQA==

Test 1 shows that a ciphertext encrypted with encrypt() can be decrypted with decrypt(). Test 2 decrypts a ciphertext encrypted with the VB code. Test 3 generates a ciphertext that can be decrypted with the VB code.

PHP AES encrypt / decrypt

$sDecrypted and $sEncrypted were undefined in your code. See a solution that works (but is not secure!):


STOP!

This example is insecure! Do not use it!


$Pass = "Passwort";
$Clear = "Klartext";

$crypted = fnEncrypt($Clear, $Pass);
echo "Encrypred: ".$crypted."</br>";

$newClear = fnDecrypt($crypted, $Pass);
echo "Decrypred: ".$newClear."</br>";

function fnEncrypt($sValue, $sSecretKey)
{
return rtrim(
base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_256,
$sSecretKey, $sValue,
MCRYPT_MODE_ECB,
mcrypt_create_iv(
mcrypt_get_iv_size(
MCRYPT_RIJNDAEL_256,
MCRYPT_MODE_ECB
),
MCRYPT_RAND)
)
), "\0"
);
}

function fnDecrypt($sValue, $sSecretKey)
{
return rtrim(
mcrypt_decrypt(
MCRYPT_RIJNDAEL_256,
$sSecretKey,
base64_decode($sValue),
MCRYPT_MODE_ECB,
mcrypt_create_iv(
mcrypt_get_iv_size(
MCRYPT_RIJNDAEL_256,
MCRYPT_MODE_ECB
),
MCRYPT_RAND
)
), "\0"
);
}

But there are other problems in this code which make it insecure, in particular the use of ECB (which is not an encryption mode, only a building block on top of which encryption modes can be defined). See Fab Sa's answer for a quick fix of the worst problems and Scott's answer for how to do this right.

Python AES-256-CBC Encrypt to PHP Decrypt with openssl_decrypt return false

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"

How to decrypt AES256 data which was encrypted on PHP and get value in Javascript?

The key used in the PHP code is only 20 bytes in size and thus too small for AES-256 (AES-256 requires a 32 bytes key). PHP/OpenSSL implicitly pads the key with 0x00 values to the required key length. In the CryptoJS code, this must be done explicitly.

Furthermore, in the CryptoJS code, IV (the first 16 bytes), HMAC (the following 32 bytes) and ciphertext (the rest) are not separated correctly.

Also, the authentication is missing. To do this, the HMAC for the ciphertext must be determined using the key and compared with the HMAC sent. Decryption only takes place if authentication is successful.

If all of this is taken into account, the posted code can be fixed e.g. as follows:

var key = CryptoJS.enc.Utf8.parse("9SJ6O6IwmItSRICbXgdJ".padEnd(32, "\0")); // pad key
var ivMacCiphertext = CryptoJS.enc.Base64.parse("lUIMFpajICh/e44Mwkr0q9xdyJh5Q8zEJHi8etax5BRl78Vsyh+wDknmBga1L8p8SDZA6WKz1CvAAREFGreRAQ==")

var iv = CryptoJS.lib.WordArray.create(ivMacCiphertext.words.slice(0, 4)); // get IV
var hmac = CryptoJS.lib.WordArray.create(ivMacCiphertext.words.slice(4, 4 + 8)); // get HMAC
var ct = CryptoJS.lib.WordArray.create(ivMacCiphertext.words.slice(4 + 8)); // get Ciphertext

var hmacCalc = CryptoJS.HmacSHA256(ct, key);
if (hmac.toString() === hmacCalc.toString()) { // authenticate
var dt = CryptoJS.AES.decrypt({ciphertext: ct}, key, { iv: iv }).toString(CryptoJS.enc.Utf8); // decrypt
console.log(dt);
} else {
console.log("Decryption failed");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>

Openssl aes-256-cbc encryption from command prompt and decryption in PHP (and vice versa)

The OpenSSL statement generates a random 8 bytes salt during encryption, which is used together with the password to derive a 32 bytes key and a 16 bytes IV with the OpenSSL function EVP_BytesToKey().

With key and IV the encryption is performed with AES-256 in CBC mode. The result consists of the concatenation of the ASCII encoding of Salted__, followed by the salt and the actual ciphertext, all Base64 encoded.

The decryption in PHP/OpenSSL must be implemented as follows:

  • Determination of salt and actual ciphertext.
  • Using salt, password and EVP_BytesToKey() to get key and IV.
  • Using key and IV to perform decryption with AES-256 in CBC mode.

One possible implementation is:

<?php
function EVP_BytesToKey($salt, $password) {
$bytes = '';
$last = '';
while(strlen($bytes) < 48) {
$last = hash('md5', $last . $password . $salt, true);
$bytes.= $last;
}
return $bytes;
}

$saltCiphertext = base64_decode('U2FsdGVkX18ruQUgA9LEOOvdOUQXv/o8z6ZNO820MKzSIbMjFcyfNo1efQwAOINxMY9+UxZjxaT+JEWmlUyYQw==');
$salt = substr($saltCiphertext, 8, 8);
$ciphertext = substr($saltCiphertext, 16);
$keyIv = EVP_BytesToKey($salt, 'sw8/M!CLl:=cmgtHts?v/Wb7C$Vk9Sy-{go.*+E;[GAg~KQi*rI!1#z;x/KT');
$key = substr($keyIv, 0, 32);
$iv = substr($keyIv, 32);
echo openssl_decrypt($ciphertext, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv); // {un:est@test.com,upass:klkKJS*dfd!j@d76w}
?>

In earlier versions OpenSSL used MD5 as digest in EVP_BytesToKey() by default, from version V1.1.0 SHA256. In the posted example, decryption with MD5 is successful, so obviously MD5 was used in encryption.

Note that key derivation with EVP_BytesToKey() is deemed insecure nowadays.



Related Topics



Leave a reply



Submit