PHP simple string encryption and decryption - php

Is there a simple function in PHP other than mcrypt() that can encrypt and decrypt a string.
I was trying out the code below, but that's too much for what I'm trying to do.
I'm trying to encrypt page numbers that are sent with a URL, so users will not be able to access a page simply by making changes to the page number in the browsers location bar. My page number has some other data too, that I do not want visible to users.
Example:
http://www.example.com/p10:05 to http://www.example.com/895f852d22d558esc23
I don't need such high level encryption and decryption like in the code below. Just something that can do like in my example is sufficient.
Another reason I do not like using mcrypt, is because of the 2 == it adds to the end of a string.
$salt ='iodine';
function simple_encrypt($text)
{
global $salt;
return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
}
function simple_decrypt($text)
{
global $salt;
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt, base64_decode($text), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}
echo simple_encrypt('Hello')

MCrypt does not add those == characters to the string, the base 64 encoding does. It is possible to simply remove them. Just make sure that the base64 string is a multiple of 4 characters by adding them back again when the string is received.
Base 64 can contain the '/' and '+' characters by default (depending on the input). Replace them with URL safe - and _ characters.
The code shows MCRYPT_RIJNDAEL_256 which is not AES; it is Rijndael with a 256 bit block size. Using MCRYPT_RIJNDAEL_128 - which is AES - would be better. This still allows the code to encrypt up to 16 character number values and it will decrease the output size.
There is no need to generate an IV if ECB mode is used, so remove that part of the method. There is no need to add unnecessary work for the system random number generator.
The $salt value is actually the key value, better name it as such to avoid confusion.

Related

Encrypt decrypt issue with new php version

I have a encrypt function on one of my website which is running on PHP 5.3.29
The function works proper on this version of PHP. The function is:
function encrypt($text) {
return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, SALT, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
}
I have another website which is running on PHP 5.6.29. The same function does not return anything on this version. It returns blank.
Similarly I have decrypt function which is also not working on PHP 5.6.29
function decrypt($text) {
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, SALT, base64_decode($text), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}
I need to make this function to work on PHP 5.6.29 as my websites are connected via API. I have no idea how I can make this to work. Any help please?
You're probably passing an incorrect value for SALT. From the manual:
Invalid key and iv sizes are no longer accepted. mcrypt_encrypt() will now throw a warning and return FALSE if the inputs are invalid. Previously keys and IVs were padded with '\0' bytes to the next valid size.
This was a change made to PHP 5.6, which lines up with what you're seeing.
Note that an encryption key is not the same as a hashing salt, which can generally be of any length.
I think you have problem with SALT. If you will give 32 character SALT then these functions will give output in PHP 5.6.29 as well.

mcrypt_encrypt(): Key of size 29 not supported by this algorithm

i have my old code back from 2011 which calculate hash
private static $key = 'G#W351T35.cz#€2011GAMESITES';
/**
* Computes salted password hash.
* #param string
* #return string
*/
public static function calculateHash($password)
{
$text = $password;
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, self::$key, $text, MCRYPT_MODE_ECB, $iv);
return base64_encode($crypttext);
}
When i try to run it now I get an error:
Warning: mcrypt_encrypt(): Key of size 29 not supported by this
algorithm. Only keys of sizes 16, 24 or 32 supported in ..\Hash.php
on line 27
I know it takes a long time from 2011 and there can be better ways to do it now, but I need to make it work from previous version for some historical issue. What i am doing wrong?
I cant even see what size 29 does it mean.
Or alternativly is there a way how to break a hash if I still got a function?
with this i can potencialy start using new way of calculating hash.
Thanks for any advise
If you consult the changelog in the documentation for mcrypt_encrypt, you should see that since PHP 5.6.0...
Invalid key and iv sizes are no longer accepted. mcrypt_encrypt() will now throw a warning and return FALSE if the inputs are invalid. Previously keys and IVs were padded with '\0' bytes to the next valid size.
The solution is therefore to replace your key by one that is padded with null characters to 32 bytes.
Unfortunately, there is a non-ASCII character in there (the euro sign), so there are multiple possibilities how that is supposed to be encoded. It's probably best to manually encode this character. In Unicode, the euro sign has codepoint U+20AC, which would translate to '\xE2\x82\xAC' (which explains why mcrypt counts 29 bytes instead of 27), making your new key
private static $key = 'G#W351T35.cz#\xE2\x82\xAC2011GAMESITES\0\0\0';
Note that we have to assume some character encoding for your code; I have assumed UTF-8. It's unlikely but possible that, in 2011, it was supposed to be encoded in another character encoding (e.g. ISO-8859-1), which results in a very different encoding for the euro sign.
$keyis the key and must be a supported size of 16, 24 or 32 bytes in length. You are passing a length of 29 bytes, you need to use a key of appropriate size.
The code is not calculating a hash, it is encrypting $text.
It is using ECB mode which is not considered secure. Note that ECB mode does not take an iv $iv so there is no point in creating one. CBC mode is better and does use an iv.
If you really want to create a hash use a hash function such as SHA-256. If you need a "keyed" or salted hash use a HMAC.
Even "way back to 2011" encryption was not used to create hashes, there really isn't anything new since then.
Iterate over an HMAC with a random salt for about a 100ms duration (the salt needs to be saved with the hash). Use functions such as password_hash, PBKDF2, Bcrypt and similar functions. The point is to make the attacker spend a lot of time finding passwords by brute force.
See OWASP (Open Web Application Security Project) Password Storage Cheat Sheet.
See How to securely hash passwords, The Theory on Security Stackexchange.

PHP Encryption Method Comparison

I was recently asked to create a method for encryption / decryption of a URL string. I quickly produced a 1 liner and shot it out.
I was then provided with the code from another developer and asked my opinion. I looked at his to find a much more complex function.
My questions:
What are the specific differences here?
Are there shortfalls found in the short solution?
We are encrypting a json encoded array and passing it via query string URL.
Long solution:
public function Encrypt($message, $key = 'defaultkey') {
//Create an instance of the mcrypt resource
$td = mcrypt_module_open('tripledes', '', 'ecb', '');
//Create a random intialization vector and initialize
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, $key, $iv);
// Create a Timestamp and add it.
$T = new \DateTime('NOW');
$message = $T->format("YmdHis") . $message;
// PKCS7 Padding
//get the block size of the cipher
$b = mcrypt_get_block_size('tripledes', 'ecb');
//What is the purpose?
$dataPad = $b-(strlen($message)%$b);
$message .= str_repeat(chr($dataPad), $dataPad);
//convert to hexidec string
$encrypted_data = bin2hex(mcrypt_generic($td, $message));
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $encrypted_data;
}
Short Solution:
public function Encrypt($message, $key = 'defaultkey') {
$T = new \DateTime('NOW');
return bin2hex(mcrypt_encrypt(MCRYPT_3DES, $key, $T->format("YmdHis").$message, 'ecb'));
}
The only real difference is the padding. Triple DES is a symmetric block cipher and such only operates on a single full block (8 byte). A mode of operation like ECB enables it to encrypt many full blocks. When your data is not a multiple of the block size, it has to be padded for it to be encrypted.
MCrypt uses zero padding by default. It will fill up the plaintext with 0x00 bytes until a multiple of the block size is reached. Those additional padding bytes have to be removed during decryption (usually done with rtrim()). This means that if the plaintext ends with 0x00 bytes, those will also be removed which might break your plaintext.
PKCS#5/PKCS#7 padding on the other hand pads with a byte that represents the number of padding bytes. If the plaintext is already a multiple of the block size, it will add a full block of padding. Doing it this way enables it to only remove the padding and not additional plaintext bytes during decryption.
Whether mcrypt_generic_init() or mcrypt_encrypt() was used doesn't really make a difference.
You should never use ECB mode. It is not semantically secure. It means that the same plaintext block will always result in the same ciphertext block. Since you're encrypting URLs the first couple of blocks will stay the same for similar URLs after observing many ciphertexts. An attacker might get additional information out of this.
Use at least CBC mode with a random IV. The IV doesn't need to be hidden, so it can be easily prepended to the ciphertext and sliced off during decryption.
It is also best to have the ciphertext authenticated to detect manipulation. You can use a message authentication code such as HMAC-SHA256 with a different key. A better way would be to simply use an authenticated mode such as GCM or EAX.

Rijndael PHP encode FLASH decode

I am trying to pass some encrypted data to a flash , but I got stuck somewhere in the middle.
Im using RIJNDAEL algorithm to encode the data in PHP :
function encrypt($text){
$key = "53cded30ff7ba54d65b939fd594e3d63";
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC); //get vector size on CBC mode
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); //Creating the vector
$cryptedtext = mcrypt_encrypt (MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv); //Encrypting using MCRYPT_RIJNDAEL_256 algorithm
return $cryptedtext;
}
And im using the AS3CRYPT library to decrypt the value in flash.
The problem is that if I try to decode the value in flash or even in the demo of AS3CRYPT, it doesnt work.
I also tried to return the data from PHP encoded with base64_encode but still not working.
The output from PHP is something like : flashvar=Á žJcV—µg)7¾1´‘5{Ò<¶Ù$þS„§”
Probably I did something wrong in the PHP ...
PHP doesn't add any padding, which is likely needed.
You'll have to pad it manually, take a look at this post on PHP.net which explains one method of achieving PKCS7 padding compatibility.
Beyond that, make sure you're setting the matching confidentiality mode (CBC) and cipher within "AS3CRYPTO".

PHP: mcrypt mangles beginning of string to garbage

I need medium to strong encryption on serverside, so I thought I would use mcrypt with PHP. If I use the functions below the beginning of my original string turns into binary garbage after decryption. (This is not the usual problem of getting appended additional garbage, instead my string is altered.) According to the documentation, mcrypt_encrypt() should have padded enough characters to match the block size of the selected algorithm but I suspect it does not work.
However, if I pad it manually to the block size of 128 bit (16 bytes) of Rijndael, it doesn't work either. The only way I can get this to work is by prepending some string long enough to (likely) cover the garbaged block and add a known prefix like "DATA#" between that string and my data. After decryption that block has been partially mangled but my prefix and all data after that has been correctly decrypted.
$GLOBALS['encryptionmarker'] = 'DATA#';
function encrypt($plain, $key) {
/*
// workaround because beginning of decrypted string is being mangled
// so we simply prefix with some text plus marker
$prefix = str_pad('', 128, '#', STR_PAD_RIGHT).$GLOBALS['encryptionmarker'];
$plain = $prefix.$plain;
*/
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $plain, MCRYPT_MODE_CFB,
mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CFB),
MCRYPT_DEV_URANDOM));
return $encrypted;
}
function decrypt($encrypted, $key) {
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CFB,
mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CFB),
MCRYPT_DEV_URANDOM));
/*
// workaround: remove garbage
$pos = strpos($decrypted, $GLOBALS['encryptionmarker']);
$decrypted = trim(substr($decrypted, $pos + strlen($GLOBALS['encryptionmarker'])));
*/
return $decrypted;
}
What's wrong with my functions? Why do I have to prefix my data like that (I consider it a dirty workaround, so I would like to fix it)?
Storing the encrypted data is not the problem; decrypting it immediately after encryption without storing it to a database results in the same errors.
Your problem is that you are generating a new, different, random IV on the receiving side. This doesn't work, as you've seen.
The receiver needs to know the IV that the sender used; so you have to send it along with the encrypted data and pass it to mcrypt_decrypt().
Note that you must also use mhash() with a key (a different key to the encryption key) to generate an HMAC over the message, and check it on the receiving side. If you do not, a man-in-the-middle can trivially modify parts of your message without you detecting it.
Use the same IV in en- and decryption. The IV is not a shared secret, but has to be shared. You may consult Wikipedia: IV
$IV = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CFB),
MCRYPT_DEV_URANDOM));
The IV must be transferred ONCE. You may want to increment the value of IV for each packet. But this can be done on both sides independently.

Categories