Remove Special character when decrypt by editing encrypt string - php

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

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.

SHA1 the PHP mcrypt_decrypt result

I have 2 encrypt & decrypt functions using PHP mcrypt library.
public function encrypt_string($input, $key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$cipher = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $input, MCRYPT_MODE_CBC, $iv);
return base64_encode($iv . $cipher);
}
public function decrypt_string($input, $key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$ciphertext = base64_decode($input);
$iv = substr($ciphertext, 0, $iv_size);
$cipher = substr($ciphertext, $iv_size);
return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $cipher, MCRYPT_MODE_CBC, $iv);
}
Given that the key is generated by:
$key = pack('H*', 'dfgsdighsdfksdhfosdfasdjldsfsdfgdfkgdl'); // a random key
I can successfully obtain back the input after encryption & decryption.
Here is the code:
$pass = '123456';
echo sha1($pass) . PHP_EOL; // prints 7c4a8d09ca3762af61e59520943dc26494f8941b
$pass_cipher = encrypt_string($pass, $key);
$pass_decrypt = decrypt_string($pass_cipher, $key);
echo $pass_decrypt . PHP_EOL; // prints 123456
echo sha1($pass_decrypt) . PHP_EOL; // prints f41b44dbecccaccfbb4ccf6a7fc4921c03878c6d
However, the SHA1 result is different:
7c4a8d09ca3762af61e59520943dc26494f8941b // before encrypt & decrypt
f41b44dbecccaccfbb4ccf6a7fc4921c03878c6d // after encrypt & decrypt
Why is it different ? What did I miss ?
UPDATE:
The accepted answer is useful. For people who wants additional information, here it is:
echo bin2hex($pass) . PHP_EOL; // prints 313233343536
echo bin2hex($pass_decrypt) . PHP_EOL; // prints 31323334353600000000000000000000
and after trim(), the SHA1 result works as expected, as empty hidden 0 are removed.
Problem is that your decrypt_string returns 16 bytes string, that is filled with 0 bytes at the right side. It's a problem known for about 2 years.
Remove null bytes from the right with line similar to this one:
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $cipher, MCRYPT_MODE_CBC, $iv), "\0");
Be careful not to encrypt things with null character at the end, as cryptology functions in PHP works as if all strings were null-terminated and are not shy to cut string at first \0 or to return a bit of \0s glued to the end of their output.
in post encrypted data + sign will be replaced with whitespace. thats why decryption was not done .

Is it safe to trim a decrypted string?

I am encrypting and decrypting a string using:
$key = 'my key';
$data = 'my string';
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv);
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
$data = trim($decrypted, chr(0));
http://codepad.viper-7.com/1JgCRs
Is it safe to just trim off the padding added by the encryption algorithm, or is it necessary to store the length of the data before encrypting?
You are trimming the value after you decrypt so you won't run into ay issues with the current code.
If you try to re-encrypt the different, trimmed data, you will get a different encrypted value.
Padding is added on the right normally, so consider rtrim():
$data = rtrim($decrypted, chr(0));
However this is still not yet perfectly safe because in PHP strings can contain NUL-bytes. If for some reason the plain did had NUL-bytes at the end, the rtrim will remove the padding and those previous NUL-bytes.

Encrypting IV with string and decrypting it?

Tonight I've pretty much gone through a crash course on basic cryptography, and I figured I don't want to send an IV through clearly, so I'm encrypting it with the string.
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$encrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key1, $string, MCRYPT_MODE_CBC, $iv);
$string = $iv.$encrypt;
$encrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key2, $string, MCRYPT_MODE_ECB);
$return = urlencode(base64_encode($encrypt));
and to decrypt I've got this, but it's not working
$encrypted = base64_decode(urldecode($_GET['password']));
$encrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key2, $encrypted, MCRYPT_MODE_ECB);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mb_strcut($encrypted, 0, $iv_size);
$password = mb_strcut($encrypted, $iv_size);
$password = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key1, $password, MCRYPT_MODE_CBC, $iv);
Can anyone see the problem with my code and explain to me what I'm doing wrong?
You don't need to encrypt your IV, it is safe to send it in clear. Usually it is prepended to the cyphertext. If you analyse the structure of CBC mode, you can see that the IV only affects the first cypher block. The "IV" for the second cypher block is the first cypher block, the "IV" for the third cypher block is the second cypher block. Any attacker will already know every cypher block, so they will already know most of the "IV"s.
The security of CBC mode lies in the key, and only in the key. An attacker with the cyphertext and the IV cannot recover the key, so there is no point n doing the extra work to encrypt the IV.
I believe you need to write:
$encrypted = base64_decode(urldecode($_GET['password']));
instead of what is there in wrong order for decryption.

How to do AES256 decryption in 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);

Categories