How to do AES256 decryption in PHP? - php

I have an encrypted bit of text that I need to decrypt. It's encrypted with AES-256-CBC. I have the encrypted text, key, and iv. However, no matter what I try I just can't seem to get it to work.
The internet has suggested that mcrypt's Rijndael cypher should be able to do this, so here's what I have now:
function decrypt_data($data, $iv, $key) {
$cypher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
// initialize encryption handle
if (mcrypt_generic_init($cypher, $key, $iv) != -1) {
// decrypt
$decrypted = mdecrypt_generic($cypher, $data);
// clean up
mcrypt_generic_deinit($cypher);
mcrypt_module_close($cypher);
return $decrypted;
}
return false;
}
As it stands now I get 2 warnings and the output is gibberish:
Warning: mcrypt_generic_init() [function.mcrypt-generic-init]: Key size too large; supplied length: 64, max: 32 in /var/www/includes/function.decrypt_data.php on line 8
Warning: mcrypt_generic_init() [function.mcrypt-generic-init]: Iv size incorrect; supplied length: 32, needed: 16 in /var/www/includes/function.decrypt_data.php on line 8
Any help would be appreciated.

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

I send to you one example,
Please, check the code, ok
$data_to_encrypt = "2~1~000024~0910~20130723092446~T~00002000~USD~F~375019001012120~0~0~00000000000~";
$key128 = "abcdef0123456789abcdef0123456789";
$iv = "0000000000000000";
$cc = $data_to_encrypt;
$key = $key128;
$iv = $iv;
$length = strlen($cc);
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'','cbc','');
mcrypt_generic_init($cipher, $key, $iv);
$encrypted = base64_encode(mcrypt_generic($cipher,$cc));
mcrypt_generic_deinit($cipher);
mcrypt_generic_init($cipher, $key, $iv);
$decrypted = mdecrypt_generic($cipher,base64_decode($encrypted));
mcrypt_generic_deinit($cipher);
echo "encrypted: " . $encrypted;
echo "<br/>";
echo "length:".strlen($encrypted);
echo "<br/>";
echo "decrypted: " . substr($decrypted, 0, $length);

Related

How to decrypt after Mcrypt deprecation?

I have updated my php version to 7.1.
I had functions where i encrypt data using mcrypt.
Now this function is deprecated.
How can i decrypt the data anyway withoud going back to older versions of php.
This is the code i used:
public function encrypt($plaintext) {
$ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
$ciphertext = mcrypt_encrypt(self::CIPHER, $this->key, $plaintext, self::MODE, $iv);
return base64_encode($iv.$ciphertext);
}
public function decrypt($ciphertext) {
$ciphertext = base64_decode($ciphertext);
$ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
if (strlen($ciphertext) < $ivSize) {
throw new Exception('Missing initialization vector');
}
$iv = substr($ciphertext, 0, $ivSize);
$ciphertext = substr($ciphertext, $ivSize);
$plaintext = mcrypt_decrypt(self::CIPHER, $this->key, $ciphertext, self::MODE, $iv);
return rtrim($plaintext, "\0");
}
With Constants:
const CIPHER = MCRYPT_RIJNDAEL_128; // Rijndael-128 is AES
const MODE = MCRYPT_MODE_CBC;
I saw that it was recommended to use OpenSSL. That is what i will use from now on. But how can i decrypt the older data using this method?
Thanks
Edit:
I know i can use OpenSSL as alternative.
Thats what i am doing for the content from now on.
But i need to decrypt my mcrypted code from my old contents.
*Edit request #symcbean
Tried to decrypt with OpenSSL like this:
public function decrypt($ciphertext) {
$ciphertext = base64_decode($ciphertext);
if (!function_exists("openssl_decrypt")) {
throw new Exception("aesDecrypt needs openssl php module.");
}
$key = $this->key;
$method = 'AES-256-CBC';
$ivSize = openssl_cipher_iv_length($method);
$iv = substr($ciphertext,0,$ivSize);
$data = substr($ciphertext,$ivSize);
$clear = openssl_decrypt ($data, $method, $key, 'OPENSSL_RAW_DATA'|'OPENSSL_ZERO_PADDING', $iv);
return $clear;
}
Important thing to note is that mcrypt_encrypt zero-pads input data if it's not a multiple of the blocksize. This leads to ambiguous results if the data itself has trailing zeroes.
openssl_decrypt doesn't remove the zero-padding automatically, so you're left only with the possibility of trimming the trailing nulls.
Here's a trivial example:
$data = "Lorem ipsum";
$key = "1234567890abcdef";
$iv = "1234567890abcdef";
$encrypted = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
echo bin2hex($encrypted) . "\n";
$decrypted = openssl_decrypt(
$encrypted, "AES-128-CBC", $key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
echo var_export($decrypted, true) . "\n";
$result = rtrim($decrypted, "\0");
echo var_export($result, true) . "\n";
Output:
70168f2d5751b3d3bf36b7e6b8ec5843
'Lorem ipsum' . "\0" . '' . "\0" . '' . "\0" . '' . "\0" . '' . "\0" . ''
'Lorem ipsum'
I solved it.
Don't know if its the right way (guess not)
But connected remotely on a server with a lower php version.
Decrypted all the content and encrypted with OpenSSL.
Thanks for the suggestions!
I also had some problems decrypting data encrypted with mcrypt_encrypt with openssl_decrypt. The following small test encrypts a string with mcrypt and openssl (with added zero padding and without) and decrypts all strings with both methods. This example uses ECB mode but you can easily change this to CBC by adding an IV if needed.
// Setup key and test data
$key = hash("sha256", 'test', true);
$data = 'Hello World';
$enc = $dec = [];
// Encrypt with MCRYPT_RIJNDAEL_128 method
$enc['RIJ'] = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_ECB));
// Encrypt with OpenSSL equivalent AES-256
$enc['AES'] = base64_encode(openssl_encrypt($data, 'aes-256-ecb', $key, OPENSSL_RAW_DATA));
// Encrypt with OpenSSL equivalent AES-256 and added zero padding
if (strlen($data) % 8) $data = str_pad($data, strlen($data) + 8 - strlen($data) % 8, "\0");
$enc['AES0'] = base64_encode(openssl_encrypt($data, 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING));
// Decrypt all strings with MCRYPT_RIJNDAEL_128
$dec['mRIJ'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['RIJ']), MCRYPT_MODE_ECB));
$dec['mAES'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['AES']), MCRYPT_MODE_ECB));
$dec['mAES0'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['AES0']), MCRYPT_MODE_ECB));
// Decrypt all strings with OpenSSL equivalent AES-256
$dec['oRIJ'] = bin2hex(openssl_decrypt(base64_decode($enc['RIJ']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING));
$dec['oAES'] = bin2hex(openssl_decrypt(base64_decode($enc['AES']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA));
$dec['oAES0'] = bin2hex(openssl_decrypt(base64_decode($enc['AES0']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING));
// Print results
print_r($enc);
var_dump($dec);
The print_r and var_dump output is the following:
Array
(
[RIJ] => YcvcTwAMLUMBCZXu5XqoEw==
[AES] => +AXMBwkWlgM1YDieGgekSg==
[AES0] => YcvcTwAMLUMBCZXu5XqoEw==
)
array(6) {
["mRIJ"]=>
string(32) "48656c6c6f20576f726c640000000000"
["mAES"]=>
string(32) "48656c6c6f20576f726c640505050505"
["mAES0"]=>
string(32) "48656c6c6f20576f726c640000000000"
["oRIJ"]=>
string(32) "48656c6c6f20576f726c640000000000"
["oAES"]=>
string(22) "48656c6c6f20576f726c64"
["oAES0"]=>
string(32) "48656c6c6f20576f726c640000000000"
}
If you need the same encrypted string with the openssl methods as you had with mcrypt, you'll have add the zero padding manually to the string (AES0 in the example). This way you'll get the exact same encrypted and decrypted strings as before. For some additional information about the zero padding, you should look at Joe's answer here: php: mcrypt_encrypt to openssl_encrypt, and OPENSSL_ZERO_PADDING problems
If you don't want to manually add the zero padding to all new messages, you'll need different flags for decrypting the old mcrypt-encrypted messages and the new messages encrypted with openssl. For the old messages you'll have to use the OPENSSL_ZERO_PADDING flag ($dec['oRIJ'] in the example), whereas you must not use it for the openssl encrypted messages ($dec['oAES'] in the example). In my case I used this approach, because the default behaviour of openssl seems more correct to me as the mcrypt one - if you encrypt a string with 11 bytes you get a string with 11 bytes back after decrypting it. As you can see in the example, this is not the case with mcrypt or with openssl and the added zero padding. In these cases you would have to remove the trailing zeros manually to get the original data back.

Remove Special character when decrypt by editing encrypt string

$secretKey = "MYSECRETKEY";
$plaintext = 'Plain Text Will Be here';
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$ivDecode = base64_encode(mcrypt_create_iv($iv_size, MCRYPT_RAND));
$encrypted = trim(mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
substr(sha1($secretKey), 0, 32),
$plaintext,
MCRYPT_MODE_CBC,
$iv), "\0..\32");
$encrypted = $iv . $encrypted;
$ciphertext_base64 = base64_encode($encrypted);
#echo $ciphertext_base64 . "\n";
$decrypted = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128,
substr(sha1($secretKey), 0, 32),
base64_decode($ciphertext_base64),
MCRYPT_MODE_CBC,
base64_decode($ivDecode)), "\0..\32");
echo $decrypted;
when I run above code I got this output.
»_w>ø9â„6ÅkžPlain Text Will Be here
I can't edit $decrypted string because I can't access it. I just can edit $encrypted only. So how can remove extra special characters(»_w>ø9â„6Åkž) from out put by editing $encrypted string. I want to send encrypted text using JSON to the different server to decrypt it.
It is not possible to split the iv and encrypted data prior to Base64 decoding, first Base64 decode and then split them.
MCRYPT_RIJNDAEL_128 which is also AES has a block size of 128-bits or 16-bytes. The iv must be that size. Instead of including base64_decode($iv) as a parameter actually create a 16-byte iv. Base64 decoding the iv will not work if it is is not Base64 encoded, it isn't in this case.
The key should be 128, 192 or 256 bits (16, 24 or 32 bytes), exactly the correct size for interoperability, do not rely on padding by the encryption algorithms.
Similarly, for the input to be encrypted and the key prepare it in a separate statement so that debugging is easier.
Do not trim the output, the mcrypt_decrypt is correct. Padding may add an additional block, that is required.
Do not Base64 decode the result of the decryption, the plaintext was not Base64 encoded. – zaph just now edit
"text like this ïÕ[pI¤;Køv" probably occurs when attempting to print data as a string, not all binary bytes have a print representation and many have special characters as their print representation in the 0x80-0xff range.
Here is the concept, not tested, I have not used php in 20 years so fix any errors:
$secretKey = "1234567890123456"; # Note the length is 16-bytes, a full key
$plaintext = 'XXXXXXXXX';
echo $plaintext . "\n";
# --- ENCRYPTION ---
$key = substr(sha1($secretKey), 0, 32)
$iv = mcrypt_create_iv(16, MCRYPT_RAND);
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
$key,
$plaintext,
MCRYPT_MODE_CBC,
$iv);
# prepend the IV for it to be available for decryption
$ciphertext = $iv . $ciphertext;
$ciphertext_base64 = base64_encode($ciphertext);
echo $ciphertext_base64 . "\n";
# --- DECRYPTION ---
$key = substr(sha1($secretKey), 0, 32)
$cipher_text_iv = base64_decode($ciphertext_base64)
# split the iv and encrypted text
$iv = substr($cipher_text_iv, 0, 16)
$ciphertext = substr($cipher_text_iv, 16)
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128,
$key,
$ciphertext,
MCRYPT_MODE_CBC,
$iv);
echo $decrypted;

PHP AES 128 bit CBC Encryption gives a size warning

i am using the AES ALgo with 128 bit with CBC cipher mode encryption, below is the code:
$cc = 'my secret text';
$key = '3aa22e01c04c7059778c54d122b0273689fba00f4a166a66d15f7ba6a8ba8743';
$iv = '1234567890123456';
$length = strlen($cc);
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'','cbc','');
mcrypt_generic_init($cipher, $key, $iv);
$encrypted = base64_encode(mcrypt_generic($cipher,$cc));
mcrypt_generic_deinit($cipher);
mcrypt_generic_init($cipher, $key, $iv);
$decrypted = mdecrypt_generic($cipher,base64_decode($encrypted));
mcrypt_generic_deinit($cipher);
echo "encrypted: " . $encrypted;
echo "\n";
echo "decrypted: " . substr($decrypted, 0, $length) . "\n";
But this gives me a warning with output as :
Warning: mcrypt_generic_init(): Key size too large; supplied length:
64, max: 32 in /var/www/cipher.php on line 10 Warning:
mcrypt_generic_init(): Key size too large; supplied length: 64, max:
32 in /var/www/cipher.php on line 14 encrypted:
vM/XVYSjs/QApdCUEQ8bdQ== decrypted: my secret text
Now can someone guide me on why I am facing the size issue and how I can eradicate the issue of size without changing the key...
You are doing 128-bit encryption but your key is 64 hex characters long, which is 265 bits.
Either you need to be doing 256 bit encryption or your key needs to half the size.
I modify your code and it runs fine,
Please, check my changes, ok
$data_to_encrypt = "2~1~000024~0910~20130723092446~T~00002000~USD~F~375019001012120~0~0~00000000000~";
$key128 = "abcdef0123456789abcdef0123456789";
$iv = "0000000000000000";
$cc = $data_to_encrypt;
$key = $key128;
$iv = $iv;
$length = strlen($cc);
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'','cbc','');
mcrypt_generic_init($cipher, $key, $iv);
$encrypted = base64_encode(mcrypt_generic($cipher,$cc));
mcrypt_generic_deinit($cipher);
mcrypt_generic_init($cipher, $key, $iv);
$decrypted = mdecrypt_generic($cipher,base64_decode($encrypted));
mcrypt_generic_deinit($cipher);
echo "encrypted: " . $encrypted;
echo "<br/>";
echo "length:".strlen($encrypted);
echo "<br/>";
echo "decrypted: " . substr($decrypted, 0, $length);

Issue with decrypting aes password in php

I am working on some PHP code to perform AES string encryption and decryption. The encryption is working fine but I can't seem to decrypt it.
Below is the code that does the encryption and adds the required padding.
function encrypt($data)
{
$iv = "PRIVATE";
$key = CIPHERKEY;
return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, addpadding($data), MCRYPT_MODE_CBC, $iv));
}
function addpadding($string, $blocksize = 16)
{
$len = strlen($string);
$pad = $blocksize - ($len % $blocksize);
$string .= str_repeat(chr($pad), $pad);
return $string;
}
The code above is working fine the code below where it does the decryption keeps on failing. I try and do the decryption and then strip the padding but false is always returned from the padding function.
Below is the code that does the decryption and the stripping.
function strippadding($string)
{
$slast = ord(substr($string, -1));
$slastc = chr($slast);
$pcheck = substr($string, -$slast);
if(preg_match("/$slastc{".$slast."}/", $string)){
$string = substr($string, 0, strlen($string)-$slast);
return $string;
} else {
return "false";
}
}
function decrypt($data)
{
$iv = "PRIVATE";
$key = CIPHERKEY;
//$decoded = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
$decrytped = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
$base64Decoded = base64_decode($decrytped);
return strippadding($base64Decoded);
}
Thanks for any help you can provide.
In your decryption method, the decrypt and base64 steps are backwards. It's important that in whatever order you do your operations for encrypting, you do the reverse to decrypt.
In your encrypt method, you're base64 encoding the ciphertext:
base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, addpadding($data), MCRYPT_MODE_CBC, $iv));
When you decrypt, you need to undo the base64 encoding, and then decrypt that result.
$base64Decoded = base64_decode($data);
$decrytped = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $base64Decoded, MCRYPT_MODE_CBC, $iv);
Also, it looks like mcrypt will handle padding uneven blocks, so your addpadding() method might be superfluous.
You are using a too-short IV which is less than the blocksize. I assume this results in using a different IV on decryption. Blocksize is 16 bytes, so the IV should be 16 bytes, but your fixed-string IV is only 7 bytes (8 if you count the terminating zero byte which the underlying C code probably adds).
Instead of using a fixed string, you should be using a random IV generated by mcrypt_create_iv(). You can find out the length of one block by using mcrypt_get_iv_size() as shown in Example 1 in the mcrypt_create_iv() PHP manual. You would then send/store the random IV prepended to the ciphertext so you know what the IV was on decryption.
Also, mcrypt does its own padding, so you don't need to. And, as mfanto pointed out, you need to decode Base64 before decryption, not after.

PHP generate symmetric key with AES, ECB mode block and PKCS5Padding

I need to generate symmetrics keys with standard AES in ECB mode block and with PKCS5Padding, but I can't do it.
During my searches, I only found functions to encrypt something with this conditions above. But I don't want this; I want to generate a symmetric key.
I need to can communicate with a webservice and I need this, because is one part of encryption.
I have looked at phpseclib, but the library doesn't generate symmetric keys either.
First, to do PKCS#5 padding on the input you need to improvise:
// source: http://php.net/manual/en/ref.mcrypt.php#69782
function pkcs5_pad($text, $blocksize)
{
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
Then select your algorithm and generate the IV:
$alg = MCRYPT_RIJNDAEL_128; // AES
$mode = MCRYPT_MODE_ECB; // not recommended unless used with OTP
$iv_size = mcrypt_get_iv_size($alg, $mode);
$block_size = mcrypt_get_block_size($alg, $mode);
$iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM); // pull from /dev/urandom
Initialize your encryption key:
$key = "This is a very secret key";
Apply padding to the input and encrypt it
$input = pkcs5_pad($input, $block_size);
$crypttext = mcrypt_encrypt($alg, $key, $input, $mode, $iv);
In php I have used this to create a symmetric key.
<?php
srand((double)microtime()*1000000 );
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CFB, '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$ks = mcrypt_enc_get_key_size($td);
$key = substr(sha1('Your Secret Key Here'), 0, $ks);
mcrypt_generic_init($td, $key, $iv);
$ciphertext = mcrypt_generic($td, 'This is very important data');
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
print $iv . "\n";
print trim($ciphertext) . "\n";
?>
This would be a good starting place :
http://php.net/manual/en/function.mcrypt-create-iv.php
You can do this with a call to the phpseclib library, which can be adjusted to any cipher (AES, DES..), encryption mode, key length and optional PBKDF2 derivation.
Please see:
http://phpseclib.sourceforge.net/crypt/examples.html

Categories