I am trying to mcrypt some data using a class I created (methods below). This is how you mcrypt data then using pack, then you can use unpack to get the data back.
$packed = $server->cache->pack("packed", array(123,123,123), "Password");
if(!$packed){
echo "Could not encrypt data\n";
}
$server->cache->unpack("packed", "Password");
when I pack it, I do a var_dump on the json_encode() data, and get this:
string(13) "[123,123,123]"
When I unpack it, I do a var_dump on the mcrypt_decode() string, and get this:
string(32) "[123,123,123]"
Why are the lengths different? When I do a json_decode() on the mcrypt_decode() string, I get null back, and this is the reason. If I trim the data it works, but I shouldn't have to trim it.
Here are the methods:
<?php
public function put($key, $value, $life = 0)
{
$this->cache[$key] = $value;
$life = (int)$life;
if($life > 0)
{
$life = strtotime("now + $life seconds");
}
$this->life[$key] = $life;
}
public function get($key)
{
return $this->cache[$key];
}
public function pack($key, $value, $secret, $life = 0)
{
if(!function_exists("mcrypt_encrypt"))
{
$this->put($key, $value, $life);
return false;
}
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$value = json_encode($value);
var_dump($value);
$cryptdata = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $secret, $value, MCRYPT_MODE_ECB, $iv);
$this->put($key, $cryptdata, $life);
return true;
}
public function unpack($key, $secret)
{
if(!function_exists("mcrypt_decrypt"))
{
return json_decode($this->get($key), true);
}
$cryptdata = $this->get($key);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $secret, $cryptdata, MCRYPT_MODE_ECB, $iv);
//$data = json_decode($data, true);
var_dump($data);
}
When using a block cipher mode like ECB (you shouldn't be using that one btw), MCrypt will NUL-pad the data, so that its length is dividable by the encryption algorithm's block size.
If you must know, for Rijndael-256 the block size is 256 bits or 32 bytes.
Considering that you're encrypting JSON data, you can just rtrim() the data and not worry about it. There's no way around that unless you switch to a counter mode like CTR.
Related
I have old PHP project where used mcrypt_encrypt
But this function doesn't work in new PHP.
I try to convert it to php7+, use openssl_encrypt, but I got no same result, what I do wrong?
Maybe do you know other way to get same result?
mcrypt_encrypt function:
public static function cryptToCode($data)
{
$iv_size = #mcrypt_get_iv_size(MCRYPT_CAST_128, MCRYPT_MODE_ECB);
$iv = #mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM );
$code = #mcrypt_encrypt(MCRYPT_CAST_128, self::KEY_TRANSLATE_ID, $data, MCRYPT_MODE_ECB, $iv);
$code = bin2hex($code);
return $code;
}
openssl_encrypt function:
public static function cryptToCodeSSL($data, $key = self::KEY_TRANSLATE_ID, $method = "cast-128-ecb") //"cast-128-ecb"
{
$iv_size = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($iv_size);
$code = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
$code = bin2hex($code);
return $code;
}
Done:
I found library http://www.gilfether.com/phpcrypt/
I got same result.
I have OpenCart 1.5.6.4 with encryption.php file in system library folder.
The codes in encryption.php are :
<?php
final class Encryption {
private $key;
private $iv;
public function __construct($key) {
$this->key = hash('sha256', $key, true);
$this->iv = mcrypt_create_iv(32, MCRYPT_RAND);
}
public function encrypt($value) {
return strtr(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->key, $value, MCRYPT_MODE_ECB, $this->iv)), '+/=', '-_,');
}
public function decrypt($value) {
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->key, base64_decode(strtr($value, '-_,', '+/=')), MCRYPT_MODE_ECB, $this->iv));
}
}
?>
For migration from php 5.6 to php 7.2 , I need to replace Mcrypt Encription with OpenSSL Encription.
I have replaced mcrypt_create_iv(32, MCRYPT_RAND) with openssl_random_pseudo_bytes(32, true) , but for encrypt function and decrypt function , I do not know what parameters to use for these functions.
What changes needed in encription.php codes?
I originally wrote this to address the empty iv warning that comes up with the current encryption class for OC3:
Warning: openssl_encrypt(): Using an empty Initialization Vector (iv) is potentially insecure and not recommended
And recently backported it to work with OC1.5 for the precise reason you posted this question. Here's a complete drop in replacement for system/library/encryption.php that will work on OC1.5.6.4 and PHP7.2:
final class Encryption {
private $cipher = 'aes-256-ctr';
private $digest = 'sha256';
private $key;
public function __construct($key) {
$this->key = $key;
}
public function encrypt($value) {
$key = openssl_digest($this->key, $this->digest, true);
$iv_length = openssl_cipher_iv_length($this->cipher);
$iv = openssl_random_pseudo_bytes($iv_length);
return base64_encode($iv . openssl_encrypt($value, $this->cipher, $key, OPENSSL_RAW_DATA, $iv));
}
public function decrypt($value) {
$result = NULL;
$key = openssl_digest($this->key, $this->digest, true);
$iv_length = openssl_cipher_iv_length($this->cipher);
$value = base64_decode($value);
$iv = substr($value, 0, $iv_length);
$value = substr($value, $iv_length);
if (strlen($iv) == $iv_length) {
$result = openssl_decrypt($value, $this->cipher, $key, OPENSSL_RAW_DATA, $iv);
}
return $result;
}
}
I have 2 tables a merchant table and order table. each table has a secret key I use to compare to check security. Both are encrypted. I decrypt them to validate. The problem is after decryption I get the same string value but comparing them returns false. Here's the code
THIS ISSUE HAPPENS IF STRING HAS SPECIAL CHARACTERS AND DOESN'T HAPPEN IF STRING HAS LETTERS AND NUMBERS ONLY
public function merchant_encrypt($pure_string, $encryption_key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
return $encrypted_string;
}
public function merchant_decrypt($encrypted_string, $encryption_key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, $encryption_key, $encrypted_string, MCRYPT_MODE_ECB, $iv);
return $decrypted_string;
}
public function replace_spechial_charater($value){
$value = str_replace('+=','plusequal',$value);
$value = str_replace('=','equalsign',$value);
$value = str_replace('+','plussign',$value);
$value = str_replace('/','slashsign',$value);
$value = str_replace('&','andsign',$value);
return $value;
}
public function restore_spechial_charater($value){
$value = str_replace('plusequal','+=',$value);
$value = str_replace('equalsign','=',$value);
$value = str_replace('plussign','+',$value);
$value = str_replace('slashsign','/',$value);
$value = str_replace('andsign','&',$value);
return $value;
}
public function strhex($string) {
$hexstr = unpack('H*', $string);
return array_shift($hexstr);
}
Saving the merchant key
$enc_key = $row['merchant_id'];
$merchant_key = trim($_POST['key']); //e.g: 1234abcd+=&$
$merchant_key = replace_spechial_charater($merchant_key);
$encrypted_key = merchant_encrypt($merchant_key ,$enc_key);
$encrypted_key = base64_encode($encrypted_key);
//save $encrypted_key in the merchant table
To decrypt it
$decrypted_key = base64_decode($row['key']);
$decrypted_key = decrypt($decrypted_key,$row['merchant_id']);
$decrypted_key = restore_spechial_charater($decrypted_key);
// the result is 2d1d54rt5h4th5rh5tr1h%$&^/+=gdgdfgd
The same non-encrypted key is encrypted in the merchant website but with another enc_key
$enc_key = $row['order_id'];
$merchant_key = $row['key']; // 1234abcd+=&$
$merchant_key = replace_spechial_charater($merchant_key);
$encrypted_key = merchant_encrypt($merchant_key ,$enc_key);
$encrypted_key = base64_encode($encrypted_key);
//send $encrypted_key with other parameters to the payment gateway then returned to the php script
$order_decrypted_key = base64_decode($row['order_id']);
$order_decrypted_key = decrypt($order_decrypted_key ,$row['order_id']);
$order_decrypted_key = restore_spechial_charater($order_decrypted_key );
// the result is 2d1d54rt5h4th5rh5tr1h%$&^/+=gdgdfgd
var_dump(strip_tags($decrypted_key));
var_dump(strip_tags($order_decrypted_key));
$result = strcasecmp( trim($decrypted_key), trim($order_decrypted_key) );
echo $result;
//var_dump(trim()) returns the same result for both values
The result is:
string(39) "2d1d54rt5h4th5rh5tr1h%$&^/+=gdgdfgd"
string(35) "2d1d54rt5h4th5rh5tr1h%$&^/+=gdgdfgd"
3
Then:
$order_key = strhex($order_decrypted_key);
$merchant_key = strhex($decrypted_key);
var_dump(trim($decrypted_key));
var_dump(trim($order_decrypted_key));
string(78)
"326431643534727435683474683572683574723168252426616d703b5e2f2b3d67646764666764"
string(70)
"3264316435347274356834746835726835747231682524265e2f2b3d67646764666764"
So how to fix this issue and check if they are equal or not
Your two strings aren't the same, as everyone gathered. And there is no mystery padding I'm afraid.
Your longer string is 2d1d54rt5h4th5rh5tr1h%$&^/+=gdgdfgd
(notice the htmlentity in the middle, accounting for the 4 extra characters).
(Found by running the following on your unpacked data):
$string = "326431643534727435683474683572683574723168252426616d703b5e2f2b3d67646764666764";
$packed = pack("H*", $string);
var_dump($packed);
Response:
string(39) "2d1d54rt5h4th5rh5tr1h%$&^/+=gdgdfgd"
I can only guess that you are printing your comparison on the web instead than on the terminal, and that you are presenting the result as rendered by the browser (and not the actual result), hence not seeing those extra four characters.
Before saving your data you should probably run something to decode the html entities in your input. Or you could do it before comparing, but much better to save the data properly.
$decrypted_key = htmlspecialchars_decode($decrypted_key);
$order_decrypted_key = htmlspecialchars_decode($order_decrypted_key);
I am trying to decode a simple JSON string and the format to make it look correct. I actually copied the string and decode is with the same algorithm but without all the extra code and it worked fine.
print_r(json_decode('{"user_id":1,"issused":"2016-02-24 04:40:17","expire":"2016-03-02 04:40:17"}'));
That worked. But when I do
$hash = Hash::salt(32);
$issused = date('Y-m-d H:i:s');
$expire = date('Y-m-d H:i:s', strtotime('+1 week'));
$data = array('user_id' => 1, 'issused' => $issused, 'expire' => $expire);
$encrypt = Cipher::encrypt(json_encode($data), $hash);
$decrypt = Cipher::decrypt($encrypt, $hash);
echo $encrypt;
echo "<br><br>";
echo $decrypt;
echo "<br><br>";
print_r(json_decode($decrypt));
Where $decrypted is the valid formated JSON that I posted above. When I used:
echo json_last_erro();
It gave me an output of 3 which is JSON_ERROR_CTRL_CHAR
Any idea why this isn't being decoded correctly?
EDIT
Here is how I am encrypting data.
class Cipher {
public static function encrypt($string, $hash) {
$size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($size, MCRYPT_RAND);
$encrypted = mcrypt_encrypt(MCRYPT_BLOWFISH, $hash, utf8_encode($string), MCRYPT_MODE_ECB, $iv);
//$encoded = urlencode($encrypted);
$encoded = base64_encode($encrypted);
return $encoded;
}
public static function decrypt($string, $hash) {
//$decoded = urldecode($string);
$decoded = base64_decode($string);
$size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($size, MCRYPT_RAND);
$decrypted = mcrypt_decrypt(MCRYPT_BLOWFISH, $hash, $decoded, MCRYPT_MODE_ECB, $iv);
return $decrypted;
}
}
Here how I am creating the salt.
public static function salt($length) {
return mcrypt_create_iv($length); //base64_encode(openssl_random_pseudo_bytes($length));
}
The extra control characters (\0) are due to the cypher block padding. From the mcrypt_decrypt docs
data
The data that will be decrypted with the given cipher and mode. If
the size of the data is not n * blocksize, the data will be padded
with '\0'.
You can pad the input for the block size yourself in the encrypt and then remove the extra padding in decrypt() or you can trim the trailing zero bytes from the decoded message doing the below.
$decrypt = trim($decrypt, "\0");
Here are my encrypt and decrypt functions
public function encrypt($text){
$key = hash("md5", KEY);
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_TWOFISH, MCRYPT_MODE_CBC), MCRYPT_RAND);
$result = base64_encode(mcrypt_encrypt(MCRYPT_TWOFISH, $key, $text, MCRYPT_MODE_CBC, $iv));
return $result;
}
public function decrypt($text){
$key = hash("md5", KEY);
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_TWOFISH, MCRYPT_MODE_CBC), MCRYPT_RAND);
$result = trim(mcrypt_decrypt(MCRYPT_TWOFISH, $key, base64_decode($text), MCRYPT_MODE_CBC, $iv));
return $result;
}
When encryption is run on a JSON string to be stored as a text file and then retrieved and decrypted the front section of the resulting string has replacement and/or incorrect characters:
Expected:
{"players":[{"label":"...
Actual:
�Ӹ�!G#${�W�Rՙ�bel":"...
If it makes any difference the actual placement/incorrect chars are different each time I refresh the page on the same file.
In case anyone comes across this...
The IV needs to be prepended to the file before encoding, like so:
public function encrypt($text){
$key = hash("md5", KEY);
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_TWOFISH, MCRYPT_MODE_CBC), MCRYPT_DEV_URANDOM);
$result = base64_encode($iv.mcrypt_encrypt(MCRYPT_TWOFISH, $key, $text, MCRYPT_MODE_CBC, $iv));
return $result;
}
Then when decrypting take the IV from the decoded string and use it to decrypt, like so:
public function decrypt($text){
$key = hash("md5", KEY);
$decode = base64_decode($text);
$iv = substr($decode, 0, 16);
$decrypt = substr($decode, 16);
$result = mcrypt_decrypt(MCRYPT_TWOFISH, $key, $decrypt, MCRYPT_MODE_CBC, $iv);
return $result;
}