Encryption in PHP leaves unwanted characters - php

I have made an encryption function which encrypts a simple value and stores it in the database. Here is the code to encrypt and decrypt:
public function encrypt($string){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$value = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->key256, $string, MCRYPT_MODE_ECB, $iv);
$value = base64_encode($value);
return $value;
}
public function decrypt($string){
$value = base64_decode($string);
$value = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->key256, $value, MCRYPT_MODE_ECB);
return $value;
}
When I encrypt a simple value such as 'Michael' and decrypt again, I get the value:
Michael���������
Is there a reason I get all those question marks or a way to get rid of them?

In my experience, those extra character are NULL-bytes used for padding, that has been preserved after decryption.
You should try changing your decrypt() function to:
public function decrypt($string){
$value = base64_decode($string);
$value = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->key256, $value, MCRYPT_MODE_ECB);
return trim($value, "\0");
}

use bin2hex() instead of bas64_encode() and you can use the hex2bin() before decryption instead of base64_decode()
protected function hex2bin($hexdata)
{
$bindata = '';
for ($i = 0; $i < strlen($hexdata); $i += 2) {
$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
}
return $bindata;
}
Ofcourse you can use hex2bin() in PHP., this custom code is for compatibility with Java like platforms.
I do it this way. May be you can give a try.
Thanks

Related

php encrypting and decrypting cookies

I have the following class that I am trying to use to encrypt and decrypt my cookies. I need to store user id in cookies for the Keep Me Logged In feature so that the user stays logged in even if he closes the browser and returns later. The following function that I am using is working fine for the encryption. However, it's not working for decryption. I got this class from this question.
$key = 'alabooencryptionkey';
$iv = AES256Encryption::generateIv();
class AES256Encryption {
public const BLOCK_SIZE = 8;
public const IV_LENGTH = 16;
public const CIPHER = 'AES256';
public static function generateIv(bool $allowLessSecure = false): string {
$success = false;
$random = openssl_random_pseudo_bytes(openssl_cipher_iv_length(static::CIPHER));
if(!$success) {
if (function_exists('sodium_randombytes_random16')) {
$random = sodium_randombytes_random16();
}else{
try {
$random = random_bytes(static::IV_LENGTH);
}catch (Exception $e) {
if($allowLessSecure) {
$permitted_chars = implode('',
array_merge(
range('A', 'z'),
range(0, 9),
str_split('~!##$%&*()-=+{};:"<>,.?/\'')
)
);
$random = '';
for($i = 0; $i < static::IV_LENGTH; $i++) {
$random .= $permitted_chars[mt_rand(0, (static::IV_LENGTH) - 1)];
}
}else{
throw new RuntimeException('Unable to generate initialization vector (IV)');
}
}
}
}
return $random;
}
protected static function getPaddedText(string $plainText): string {
$stringLength = strlen($plainText);
if($stringLength % static::BLOCK_SIZE) {
$plainText = str_pad($plainText, $stringLength + static::BLOCK_SIZE - $stringLength % static::BLOCK_SIZE, "\0");
}
return $plainText;
}
public static function encrypt(string $plainText, string $key, string $iv): string {
$plainText = static::getPaddedText($plainText);
return base64_encode(openssl_encrypt($plainText, static::CIPHER, $key, OPENSSL_RAW_DATA, $iv));
}
public static function decrypt(string $encryptedText, string $key, string $iv): string {
return openssl_decrypt(base64_decode($encryptedText), static::CIPHER, $key, OPENSSL_RAW_DATA, $iv);
}
}
I am using it here for encrypting my cookie which works fine.
function setLoginCookieSession($user){
global $key, $iv;
$_SESSION['newchatapp'] = $user;
setcookie("newchatapp", AES256Encryption::encrypt($user, $key, $iv), time()+3600*24*365*10, '/');
}
And here for decrypting which is not working. It returns nothing.
function sessionUser(){
global $key, $iv;
// return (isset($_SESSION['newchatapp']))?$_SESSION['newchatapp']:AES256Encryption::decrypt($_COOKIE['newchatapp'], $key, $iv);
return AES256Encryption::decrypt($_COOKIE['newchatapp'], $key, $iv);
}
Even if I manually input try to decode the encrypted string it still returns nothing.
echo AES256Encryption::decrypt('ianXhsXhh6MWAHliZoshEA%3D%3D', $key, $iv);
This means that the decryption is not working at all. What can I do to make it work?

PHP switch from MCRYPT_MODE_ECB to AES-256-ECB

I am rewriting code to be compatible with PHP 7.2. Old code is
public function encryptPasswordOld($password, $salt)
{
$key = md5($salt);
$result = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $password, MCRYPT_MODE_ECB);
return base64_encode($result);
}
New code should be according to my research something like this
public function encryptPasswordNew($password, $salt)
{
$method = 'AES-256-ECB';
$ivSize = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($ivSize);
$key = md5($salt);
$result = openssl_encrypt($password, $method, $key, OPENSSL_RAW_DATA, $iv);
return base64_encode($result);
}
but I tried every combination of openssl_encrypt options: OPENSSL_RAW_DATA, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, OPENSSL_ZERO_PADDING, 0 and still ended up with different result as the old method returns
try to use pkcs5padding on value before encrypt. i.e.
remember the blocksize should be 8-byte based, i.e. 8/16/24 etc.
function pkcs5_pad($text, $blocksize)
{
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr(0), $pad);
}
and encrypt option should be OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, i.e.
openssl_encrypt(pkcs5_pad($value, 16), 'aes-256-ecb', $aes_key, 3, '')

Simple PHP Encryption / Decryption (Mcrypt, AES)

I'm looking for a simple yet cryptographically strong PHP implementation of AES using Mcrypt.
Hoping to boil it down to a simple pair of functions, $garble = encrypt($key, $payload) and $payload = decrypt($key, $garble).
I'm recently learning about this subject, and am posting this answer as a community wiki to share my knowledge, standing to be corrected.
Mcrypt Documentation
It's my understanding that AES can be achieved using Mcrypt with the following constants as options:
MCRYPT_RIJNDAEL_128 // as cipher
MCRYPT_MODE_CBC // as mode
MCRYPT_MODE_DEV_URANDOM // as random source (for IV)
During encryption, a randomized non-secret initialization vector (IV) should be used to randomize each encryption (so the same encryption never yields the same garble). This IV should be attached to the encryption result in order to be used later, during decryption.
Results should be Base 64 encoded for simple compatibility.
Implementation:
<?php
define('IV_SIZE', mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
function encrypt ($key, $payload) {
$iv = mcrypt_create_iv(IV_SIZE, MCRYPT_DEV_URANDOM);
$crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $payload, MCRYPT_MODE_CBC, $iv);
$combo = $iv . $crypt;
$garble = base64_encode($iv . $crypt);
return $garble;
}
function decrypt ($key, $garble) {
$combo = base64_decode($garble);
$iv = substr($combo, 0, IV_SIZE);
$crypt = substr($combo, IV_SIZE, strlen($combo));
$payload = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $crypt, MCRYPT_MODE_CBC, $iv);
return $payload;
}
//:::::::::::: TESTING ::::::::::::
$key = "secret-key-is-secret";
$payload = "In 1435 the abbey came into conflict with the townspeople of Bamberg and was plundered.";
// ENCRYPTION
$garble = encrypt($key, $payload);
// DECRYPTION
$end_result = decrypt($key, $garble);
// Outputting Results
echo "Encrypted: ", var_dump($garble), "<br/><br/>";
echo "Decrypted: ", var_dump($end_result);
?>
Output looks like this:
Encrypted: string(152) "4dZcfPgS9DRldq+2pzvi7oAth/baXQOrMmt42la06ZkcmdQATG8mfO+t233MyUXSPYyjnmFMLwwHxpYiDmxvkKvRjLc0qPFfuIG1VrVon5EFxXEFqY6dZnApeE2sRKd2iv8m+DiiiykXBZ+LtRMUCw=="
Decrypted: string(96) "In 1435 the abbey came into conflict with the townspeople of Bamberg and was plundered."
Add function to clean control characters (�).
function clean($string) {
return preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/', '', $string);
}
echo "Decrypted: ", clean($end_result);
Sentrapedagang.com
Simple and Usable:
$text= 'Hi, i am sentence';
$secret = 'RaNDoM cHars!##$%%^';
$encrypted = simple_encrypt($text, $secret);
$decrypted = simple_decrypt($encrypted_text, $secret);
codes:
function simple_encrypt($text_to_encrypt, $salt) {
return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, pack('H*', $salt), $text_to_encrypt, MCRYPT_MODE_CBC, $iv = mcrypt_create_iv($iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND))));
}
function simple_decrypt($encrypted, $salt) {
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, pack('H*', $salt), base64_decode($encrypted), MCRYPT_MODE_CBC, $iv = mcrypt_create_iv($iv_size=mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND)));
}
Class Mycrypt
Try using this class. Here. All you need to pass is key and string.
class MCrypt
{
const iv = 'fedcba9876543210';
/**
* #param string $str
* #param bool $isBinary whether to encrypt as binary or not. Default is: false
* #return string Encrypted data
*/
public static function encrypt($str, $key="0123456789abcdef", $isBinary = false)
{
$iv = self::iv;
$str = $isBinary ? $str : utf8_decode($str);
$td = mcrypt_module_open('rijndael-128', ' ', 'cbc', $iv);
mcrypt_generic_init($td, $key, $iv);
$encrypted = mcrypt_generic($td, $str);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $isBinary ? $encrypted : bin2hex($encrypted);
}
/**
* #param string $code
* #param bool $isBinary whether to decrypt as binary or not. Default is: false
* #return string Decrypted data
*/
public static function decrypt($code, $key="0123456789abcdef", $isBinary = false)
{
$code = $isBinary ? $code : self::hex2bin($code);
$iv = self::iv;
$td = mcrypt_module_open('rijndael-128', ' ', 'cbc', $iv);
mcrypt_generic_init($td, $key, $iv);
$decrypted = mdecrypt_generic($td, $code);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $isBinary ? trim($decrypted) : utf8_encode(trim($decrypted));
}
private static function hex2bin($hexdata)
{
$bindata = '';
for ($i = 0; $i < strlen($hexdata); $i += 2) {
$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
}
return $bindata;
}
}
How To Use
$var = json_encode(['name'=>['Savatar', 'Flash']]);
$encrypted = MCrypt::encrypt();
$decrypted = MCrypt::decrypt($encrypted);

PHP encryption / decryption adding padded characters

I'm trying to create a simple Encryption class but the resulting string is being padded with non-ascii characters. I've tried rtrim(), converting to utf8, etc. as mentioned in some other answers. What exactly am I missing? Here is what the characters show up when pasting the results into Notepad++
Pastebin containing the characters here. Appears as 't' in html, but copying that into notepad shows the random bits of data after it.
class Crypter implements ICrypter {
private $Key;
private $Algo;
public function __construct($Algo = MCRYPT_BLOWFISH) {
$this->Key = substr('key', 0, mcrypt_get_key_size($Algo, MCRYPT_MODE_ECB));
$this->Algo = $Algo;
}
public function Encrypt($data) {
//$iv_size = mcrypt_get_iv_size($this->Algo, MCRYPT_MODE_ECB);
//$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$blocksize = mcrypt_get_block_size('blowfish', 'ecb'); // get block size
$pkcs = $blocksize - (strlen($data) % $blocksize); // get pkcs5 pad length
$data.= str_repeat(chr($pkcs), $pkcs); // append pkcs5 padding to the data
$crypt = mcrypt_encrypt($this->Algo, $this->Key, $data, MCRYPT_MODE_ECB);
return rtrim(base64_encode($crypt));
}
public function Decrypt($data) {
$crypt = base64_decode($data);
$iv_size = mcrypt_get_iv_size($this->Algo, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypt = mcrypt_decrypt($this->Algo, $this->Key, $crypt, MCRYPT_MODE_ECB, $iv);
return rtrim($decrypt);
}
}
You need to handle the padding in the decryption same as the encryption. Here is a working example of your code:
<?php
class Crypter{
private $Key;
private $Algo;
public function __construct($Algo = MCRYPT_BLOWFISH) {
$this->Key = substr('key', 0, mcrypt_get_key_size($Algo, MCRYPT_MODE_ECB));
$this->Algo = $Algo;
}
public function Encrypt($data) {
//$iv_size = mcrypt_get_iv_size($this->Algo, MCRYPT_MODE_ECB);
//$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$blocksize = mcrypt_get_block_size('blowfish', 'ecb'); // get block size
$pkcs = $blocksize - (strlen($data) % $blocksize); // get pkcs5 pad length
$data.= str_repeat(chr($pkcs), $pkcs); // append pkcs5 padding to the data
$crypt = mcrypt_encrypt($this->Algo, $this->Key, $data, MCRYPT_MODE_ECB);
return rtrim(base64_encode($crypt));
}
public function Decrypt($data) {
$crypt = base64_decode($data);
$iv_size = mcrypt_get_iv_size($this->Algo, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypt = mcrypt_decrypt($this->Algo, $this->Key, $crypt, MCRYPT_MODE_ECB, $iv);
$block = mcrypt_get_block_size('blowfish', 'ecb');
$pad = ord($decrypt[($len = strlen($decrypt)) - 1]);
return substr($decrypt, 0, strlen($decrypt) - $pad);
}
}
$crypter = new Crypter();
$data = "Some data to encrypt";
$encryptedData = $crypter->Encrypt($data);
$decryptedData = $crypter->Decrypt($encryptedData);
echo "Decrypted Data = [$decryptedData]\n";
Notice the three lines I replaced your original Decrypt() return line with.

iOS/PHP kCCDecodeError

I can't for the life of me work out why when I encrypt something in PHP I can't then decrypt it in my iOS app, but PHP can decrypt iOS encrypted strings and decrypt/encrypt between itself.
PHP -> Obj-C FAILS.
Yes, I have looked across the net and the only solution I found was to use CBC in PHP which I'm already doing.
I'm using the FBEncryptor library for iOS and these are the encrypt/decrypt functions in PHP:
function encrypt($decrypted)
{
$iv = ''; for($i=0;$i<16;$i++){ $iv .= "\0";}
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $passKey, $decrypted, MCRYPT_MODE_CBC, $iv);
$ciphertext = base64_encode($ciphertext);
return $ciphertext;
}
function decrypt($encrypted)
{
$iv = ''; for($i=0;$i<16;$i++){ $iv .= "\0";}
$ciphertext = base64_decode($encrypted);
$plaintext = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $passKey, $ciphertext, MCRYPT_MODE_CBC, $iv);
return $plaintext;
}
From the FBEncryptor Github page (emphasis mine):
Supported encryption algorithm is AES 256 bit only.
But in your code snippet above, you're doing:
mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext, MCRYPT_MODE_CBC, $iv);
i.e. decrypting with AES 128 bit as your cipher.
You'll need to correct your code accordingly. Other common mistakes include using mismatched padding and block cipher operation modes.
I managed to work out the problem. Was a silly mistake on my part with the pass key variable. Here is my final implementation anyway for anyone else that comes across this problem:
function encrypt($decrypted)
{
$thePassKey = "12345678901234567890123456789012";
# Add PKCS7 padding.
$str = $decrypted;
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
if (($pad = $block - (strlen($str) % $block)) < $block)
{
$str .= str_repeat(chr($pad), $pad);
}
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = ''; for($i=0;$i<$iv_size;$i++){ $iv .= "\0";}
return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $thePassKey, $str, MCRYPT_MODE_CBC, $iv));
}
function decrypt($encrypted)
{
$thePassKey = "12345678901234567890123456789012";
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = ''; for($i=0;$i<$iv_size;$i++){ $iv .= "\0";}
$str = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $thePassKey, base64_decode($encrypted), MCRYPT_MODE_CBC, $iv);
# Strip PKCS7 padding.
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$pad = ord($str[($len = strlen($str)) - 1]);
if ($pad && $pad < $block && preg_match(
'/' . chr($pad) . '{' . $pad . '}$/', $str))
{
return substr($str, 0, strlen($str) - $pad);
}
return $str;
}

Categories