Why is mcrypt_encrypt giving errors? - php

I got the code below from this site. When I run it, I get
Warning: mcrypt_encrypt(): Attempt to use an empty IV, which is NOT recommend in C:\web\apache\htdocs\dev\encrypt.php on line 7
What wrong am I doing here and how do I get this right?
//Key
$key = 'SuperSecretKey';
//To Encrypt:
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, 'I want to encrypt this', MCRYPT_MODE_CFB);
//To Decrypt:
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CFB);
echo $encrypted;
echo "<br>";
echo $decrypted;

It is not error its warning, it clearly explained in Documentation
IV - Used for the initialization in CBC, CFB, OFB modes, and in some algorithms in STREAM mode. If you do not supply an IV, while it is needed for an algorithm, the function issues a warning and uses an IV with all its bytes set to "\0".

just take a look at the documentation.
IV is the fifth parameter of mcrypt_encrypt.
Used for the initialization in CBC, CFB, OFB modes, and in some algorithms in STREAM mode. If you do not supply an IV, while it is needed for an algorithm, the function issues a warning and uses an IV with all its bytes set to "\0".

Same code with a IV (Initialization Vector) with all bytes to 0
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, 'I want to encrypt this', MCRYPT_MODE_CFB, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CFB, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");

Related

Failing to upgrade mcrypt to openssl decryption in PHP

I'm upgrading a legacy application from PHP 7.0 to 7.2 and my decrypt function isn't working when I switch out mcrypt for openssl.
I tried the existing answers like mcrypt is deprecated, what is the alternative?, and Gists like https://gist.github.com/odan/c1dc2798ef9cedb9fedd09cdfe6e8e76, but I'm still not able to make the code work.
Can anyone shed some light what I'm doing wrong?
Old code
function decrypt($value, $key) {
$ivLength = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = substr($value, 0, $ivLength);
return rtrim(
mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
hash('sha256', $key, true),
substr($value, $ivLength),
MCRYPT_MODE_CBC,
$iv
),
"\0"
);
}
New code (not working with existing inputs)
function decrypt($value, $key) {
$ivLength = openssl_cipher_iv_length('AES-128-CBC');
$iv = substr($value, 0, $ivLength);
// Note: $key is hashed because it was hashed in the old encrypt function below
return openssl_decrypt(substr($value, $ivLength), 'AES-128-CBC', hash('sha256', $key, true), OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
}
For context, here's how the old code encrypted the values:
function encrypt($value, $key) {
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_DEV_URANDOM);
return $iv . mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
hash('sha256', $key, true),
$value,
MCRYPT_MODE_CBC,
$iv
);
}
Also, the raw value in $value is twice as long when output to the error log in the new code than the old code and looks similar but with more characters.
The main issue here is the key size. You're creating the key with SHA256 which returns a 256 bit hash, therefore you're using AES / Rijndael with a 256 bit key.
The number in Rijndael-128 defines the block size, the key size is determined by the length of the key we use. The number in AES-128 defines the key size (the block size is constant, 128 bits), and if the actual key length is different than this number, then the key is shortened or expanded (with zero bytes) to fit the selected key size.
This means that your mcrypt code uses Rijndael-128 (AES) with a 256 bit key, in CBC mode. The openssl equivalent is AES-256-CBC, and if we use this algorithm then mcrypt and openssl should produce compatible results.
function decrypt_mcrypt_with_openssl($value, $key) {
$iv = substr($value, 0, 16);
$ciphertext = substr($value, 16);
$key = hash('sha256', $key, true);
$plaintext = openssl_decrypt(
$ciphertext, 'AES-256-CBC', $key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
$iv
);
return rtrim($plaintext, "\0");
}
SHA256 is not suitable as a key derivation function. If you're deriving your key from a password you can use hash_pbkdf2 with a random salt and a high enough number of iterations. Or you could create a cryptographically secure pseudo-random key with openssl_random_pseudo_bytes.
I think it's best to stop using mcrypt completely and use only openssl. Mcrypt doesn't support PKCS7 padding, it doesn't provide any authenticated encryption algorithms, and it is not being maintained anymore.

Weak Cryptography (broken algorithm) error

I have a function that I use for encoding which is like this:
$skey = "whateveryoulike"; // you can change it
if(!$value){return false;}
$text = $value;
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $skey, $text, MCRYPT_MODE_ECB, $iv);
but thing is when I run PHP RIPS scanner for the sake of PCI I get an error on the following line that says: Weak Cryptography (broken algorithm)
--> $crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $skey, $text, MCRYPT_MODE_ECB, $iv);
Is there anyone who can point me which Cryptography should I use here. Thanks in advance.
MCRYPT_RIJNDAEL_256 is not AES, it is Rijndael with a block size of 256-bits, not a key size of 256-bits. It is best to use AES which has a block size of 128-bits which is RIJNDAEL_128.
Insure th ekey is exactly the correct size, 16, 24 or 32 bytes in length, other wist the function will reject the key or pad it with something, there is no standard for key padding.
Do not use ECB mode, it is insecure, see ECB mode, scroll down to the Penguin. Instead use CBC mode with a random IV, just prefix the encrypted data with the IV for use in decryption.
mcrypt does not support PKCS#7 padding, only non-standard null padding that can't even be used with binary data.
This finally resolved the issue:
Increasing key length from 15 to 16 because it has to be 16, 24 or 32
$skey = "whateveryoulike";
to
$skey = "whateveryoulikek";
and then:
from:
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $skey, $text, MCRYPT_MODE_ECB, $iv);
to:
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $skey, $text, MCRYPT_MODE_CFB, $iv);

Message: mcrypt_encrypt(): The IV parameter must be as long as the blocksize

this is the first time I have come across this error and I am very confused as to how to fix this error. I will post my code below. Kindly request for whatever else is needed to facilitate in this. Any help will be greatly appreciated.
function encryptAes($string, $key)
{
$string = $this->addPKCS5Padding($string);
$crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $key);
return strtoupper(bin2hex($crypt));
}
UPDATE: code on line number 777
function _encode_crypt($post = NULL)
{
return "#".$this->encryptAes($post,$this->encryption_password);
}
I'm going to assume the error you're getting is accurate and that you've actually made an honest mistake in having your encryption key ($key) and your initialisation vector (also $key) as the same value. This is wrong, in so many ways...
Instead try this:
$iv = mcrypt_create_iv(
mcrypt_get_iv_size(
MCRYPT_RIJNDAEL_128,
MCRYPT_MODE_CBC
),
MCRYPT_DEV_URANDOM
);
encryptAes('blah blah', 'some super secret key', $iv);
function encryptAes($string, $key, $iv)
{
$string = $this->addPKCS5Padding($string);
$crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $iv);
return strtoupper(bin2hex($crypt));
}
You will need to store this initialisation vector somewhere to be able to decrypt.
Obviously, the $key value you were passing through was the incorrect length for the initialisation vector of the encryption algorithm you are using. You are using MCRYPT_RIJNDAEL_128 and as such the iv size should be 128 bits (16 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