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.
Related
Can you please give me a full example of how to create an encryption and decryption in PHP language? I use hexa for the data and the key. I search through google and find that there is one website that match my expectation which is here.
Take this for example:
Data: 225551100012FFFF
Key: DC1C1F2B180F85D8D522A75D2354ED149A5B81F198387B51
When I decrypt, I got 389da227862957c4
Thank you in advance!
Have found my answer from this website http://www.isapp.it/en/menu-en/31-tips-a-tricks/php/118-php-how-to-encrypt-text-in-triple-des-ecb.html
But because i want to encrypt and decrypt it using hexa, i modify the code a bit to this
function cryptECB($crypt, $key) {
//Omit hex2bin and bin2hex if plain text is used
$crypt = hex2bin($crypt);
$key = hex2bin($key);
$iv_size = mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$cryptText = mcrypt_encrypt(MCRYPT_3DES, $key, $crypt, MCRYPT_MODE_ECB, $iv);
return bin2hex($cryptText);
}
function decryptECB($encrypted, $key) {
//Omit hex2bin and bin2hex if plain text is used
$encrypted = hex2bin($encrypted);
$key = hex2bin($key);
$iv_size = mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$stringText = mcrypt_decrypt(MCRYPT_3DES, $key, $encrypted, MCRYPT_MODE_ECB, $iv);
return bin2hex($stringText);
}
So as time moves on mcrypt will go in PHP 7.2.
Of course there is an alternative: openssl.
I find it difficult to switch from mcrypt to openssl, using AES 256 CBC and preserving IVs. I am sort of new to cryptography, so I don't really know everything, but I understand the basics.
Let's say I have the following code
function encrypt($masterPassword, $data)
{
$keySize = mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
$key = mb_substr(hash('SHA256', $masterPassword), 0, $keySize);
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv);
return base64_encode($iv . $encrypted);
}
function decrypt($masterPassword, $base64)
{
$keySize = mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$key = mb_substr(hash('SHA256', $masterPassword), 0, $keySize);
$data = base64_decode($base64);
$iv = substr($data, 0, $ivSize);
$encrypted = substr($data, $ivSize, strlen($data));
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
return trim($decrypted);
}
How can I "convert" this code to use openssl insted of mcrypt?
You can't convert it, because Rijndael-256 is not AES-256, and the OpenSSL extension doesn't ship with Rijndael-256 support.
AES-256 is Rijndael-128 with a 256-bit (32-byte) key.
Unfortunately, you'll have to re-encrypt all of your data.
Edit: Also, the scheme you're currently using has some problems:
It lacks authentication (HMACs are the easiest way to do it in PHP)
It lacks proper padding (mcrypt pads with zero bytes; you need something like PKCS#5 padding instead), which is required for block mode encryption to be safe.
It's not byte-safe (you're using mb_substr())
The good news is that OpenSSL will do PKCS#5 padding for you automatically, but you should go even further and use a solid encryption library like defuse/php-encryption.
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);
$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;
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.