PHP Aes Encrypt/Decrypt

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.

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.

AES encrypt in Terminal and decrypt in PHP

OpenSSL uses a proprietary KDF that you probably don't want to put the effort in to reproduce in PHP code. However, you can pass your key as pure hex instead, avoiding the KDF, by using the -K flag:

echo 'blah' | openssl enc -aes-256-cbc -K 0000000000000000000000000000000000000000000000000000000000000000

Here, the large hex string is your 256-bit key, hex encoded. This encryption operation will be compatible with your PHP.

How to decrypt by Python for AES encrypt by PHP

Modified PHP code

$key = 'pqrstuvwxyz$abcdefghijAB12345678';

I got new encrypted string is:

4a3d8b7449fb45c521b04314382670e9c4255682cde4fda51d715a3631718ccb

Then, I modified Python code for key,

key = "pqrstuvwxyz$abcdefghijAB12345678"

I received the below.

b'abcdefghijklmnopqrstuvwxyz\x06\x06\x06\x06\x06\x06'

I added to remove

unpad = lambda s: s[:-ord(s[len(s) - 1:])]
unpad(decrypted).decode('utf-8')

I resolved!!

How to convert AES Decrypt from Javascript to php

As mentioned in the other answer, in the context of AES, CryptoJS can process both, a key (16, 24 or 32 bytes) or a password. This depends on the type of the second parameter in CryptoJS.AES.encrypt()/decrypt(). A WordArray is interpreted as a key, a string as a password.

In the current case a string is passed, which is thus processed as a password. CryptoJS generates an 8 bytes salt during encryption and derives a 32 bytes key and a 16 bytes IV from the salt and the password. The key derivation function is the OpenSSL function EVP_BytesToKey(), which additionally requires a digest and an iteration count and for which CryptoJS uses the values MD5 and 1.

A possible implementation of EVP_BytesToKey() in PHP for creating a key/IV pair for AES-256/CBC is:

// from: https://gist.github.com/ezimuel/67fa19030c75052b0dde278a383eda1b
function EVP_BytesToKey($salt, $password) {
$bytes = '';
$last = '';

// 32 bytes key + 16 bytes IV = 48 bytes.
while(strlen($bytes) < 48) {
$last = hash('md5', $last . $password . $salt, true);
$bytes.= $last;
}
return $bytes;
}

CryptoJS uses the OpenSSL format for the ciphertext, which consists of the ASCII encoding of Salted__ followed by the 8 bytes salt and the actual ciphertext. The posted ciphertext is the Base64 encoding of this value. During decryption, the salt and actual ciphertext must be separated:

// Separate salt and ciphertext
$dataB64 = 'U2FsdGVkX1+S8UNrljj2STY8bBrYmr1qUbD2GYuJgIja1rzXY2y4BBkTf9GQxUGNyfRxP/BxiGIU7EFjnA2nTrM06ySr9bJySTjDDTqlDnY=';
$data = base64_decode($dataB64);
$salt = substr($data, 8, 8);
$ciphertext = substr($data, 16);

Now the key and IV can be determined as follows:

// Derive key and iv
$passphrase = '87434313.47913419';
$keyiv = EVP_BytesToKey($salt, $passphrase);
$key = substr($keyiv, 0, 32); // hex encoded: e8db19b984ed9196fff1ce9150b73eafc4cb13abe69e6dcc1ea1528dd88982ff
$iv = substr($keyiv, 32, 16); // hex encoded: 47e26ab2bf3b66eda871d4929cc91029

and the actual ciphertext can be decrypted, e.g. with phpseclib:

use phpseclib\Crypt\AES;

$cipher = new AES('cbc');
$cipher->setKey($key);
$cipher->setIV($iv);
$plaintext = $cipher->decrypt($ciphertext);
echo json_decode($plaintext); // https://xcdn-209.bato.to/7002/32e/60af5c1a22f39459ededde23/

Since CryptoJS applies the OpenSSL format, the ciphertext is compatible with OpenSSL and can also be decrypted as follows:

openssl enc -d -aes-256-cbc -p -pass pass:87434313.47913419 -md md5 -A -a -in <file containing the U2FsdGVk...>

Note that EVP_BytesToKey() is deemed insecure. The more secure way is to use a reliable key derivation function like Argon2 or PBKDF2 (the latter is also supported by CryptoJS) to derive key and IV and perform encryption with these values.



Related Topics



Leave a reply



Submit