mcrypt_encrypt adds unwanted bytes - php

I'm trying to encrypt some data using the MCRYPT_RIJNDAEL_128 algoritm. But somehow when I encrypt and afterwards decrypt, the decrypted data has randomly added bytes.
The input: string(13) "test#test.com"
The output: string(16) "test#test.com"
As you can see the output has 16 characters while the input has 13.
The following is the code used for encryption.
class Cipher
{
/**
* The key/salt(bin2hex format) used to encrypt data with in AES(RIJNDAEL) format.
* #var string
*/
private static $encryptionKey = 'baafbd1f8d752d920caae00ae550be8185c1183207a142c97c36fca3edc507da';
/**
* Gets and transforms the encryption key to binary format.
* #return string (in binary format)
*/
public static function getEncryptionKey()
{
return hex2bin(self::$encryptionKey);
}
/**
* Generates a new random main encryption key used to encrypt data.
* Store the generated key in the private property of this class.
* #param bool $strong Whether the encryption will be strong.
* #return string The generated key in hexadecimal format.
*/
public static function generateEncryptionKey($strong = true)
{
$keySize = mcrypt_get_key_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
return bin2hex(openssl_random_pseudo_bytes($keySize, $strong));
}
/**
* Creates an encryption key IV used to store near the database record for the encrypted data.
* Use bin2hex function for a representational string.
* #return string (in binary format)
*/
public static function createIv()
{
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
return mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
}
/**
* Encrypts a given string by the generated encryption key and generated IV.
* #param string $string The string which will be encrypted.
* #param string $iv The dynamic key used to encrypt.
* #return string (in binary format)
*/
public static function encrypt($string, $iv)
{
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, self::getEncryptionKey(), $string, MCRYPT_MODE_CBC, $iv);
}
/**
* Decrypts a given string by the generated encryption key and generated IV.
* #param string $string The binary string which will be decrypted.
* #param string $iv The dynamic key which belongs to the encrypted string.
* #return string The decrypted string.
*/
public static function decrypt($string, $iv)
{
return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, self::getEncryptionKey(), $string, MCRYPT_MODE_CBC, $iv);
}
}
The encryption key showed here isn't used in production or testing environments and is only used for display purposes.
The following is used to display the decryption
$iv = Cipher::createIv();
$emailAddress = Cipher::encrypt('test#test.com', $iv);
var_dump(Cipher::decrypt($emailAddress, $iv));exit;
I'm using the following environments:
Ubuntu: 14.10
PHP: PHP 5.5.9-1ubuntu4.3 (cli) (built: Jul 7 2014 16:36:58)

Those are just padding 0x00 characters added at the end, because the string's length for that cryptographic algorithms has to be a multiple of 16 (with 128 bit).
Indeed, if you add at the end of your code:
var_dump(bin2hex(Cipher::decrypt($emailAddress, $iv)));
You can see that the last 6 characters are all 0's (which means there are 3 0x00 bytes at the end).
To remove them, just run:
$decrypted = rtrim($decrypted, "\0");

Related

PHP 7.2 : Replace mcrypt method "mcrypt_get_iv_size"

I've updated my existing system to PHP 7.4 and mcrypt has been removed and I'm looking for an alternative.
It's recommended to change the code using openssl, but I didn't see any articles posted in my case.
It is recommended to rewrite mcrypt_create_iv for random_bytes and mcrypt_ecb for mcrypt_encrypt, but there is no alternative to E and I can't think of that rewrite.
Do you have an idea?
Service.php
/**
* 3DES encryption
*
* #param string $plain Plaintext
* #return string cipher Ciphertext
*/
public function encrypt3DES($plain) {
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ECB), MCRYPT_RAND);
$cipher = bin2hex(mcrypt_ecb (MCRYPT_3DES, self::AUTH_KEY, $plain, MCRYPT_ENCRYPT, $iv));
return $cipher;
}
/**
* 3DES decryption
*
* #param string $cipher Ciphertext
* #return string Plaintext
*/
public function decrypt3DES($cipher) {
if (!ctype_xdigit($cipher)) {
return '';
}
$iv = mcrypt_create_iv (mcrypt_get_iv_size (MCRYPT_3DES, MCRYPT_MODE_ECB), MCRYPT_RAND);
$plain = mcrypt_ecb(MCRYPT_3DES, self::AUTH_KEY, pack("H*", $cipher), MCRYPT_DECRYPT, $iv);
return trim($plain);
}

OpenCart Issue With Cardinity Payment openssl_encrypt():

Good evening everyone!
I am getting a strange warning while trying to buy via credit card. Am using Cardinity payment gateway. OpenCart 3.0.2.0. PHP 7.2.0. Quickcheckout by MarketInSG. What happens is when you click on button Pay Now, it loads for a couple of seconds and then button is returned back to initial Pay Now state. Here is warning message:
PHP Warning: openssl_encrypt(): Using an empty Initialization Vector (iv) is potentially insecure and not recommended in .../system/library/encryption.php on line 23.
My encryption.php looks like this:
<?php
/**
* #package OpenCart
* #author Daniel Kerr
* #copyright Copyright (c) 2005 - 2017, OpenCart, Ltd. (https://www.opencart.com/)
* #license https://opensource.org/licenses/GPL-3.0
* #link https://www.opencart.com
*/
/**
* Encryption class
*/
final class Encryption {
/**
*
*
* #param string $key
* #param string $value
*
* #return string
*/
public function encrypt($key, $value) {
return strtr(base64_encode(openssl_encrypt($value, 'aes-256-cbc', hash('sha256', $key, true))), '+/=', '-_,');
}
/**
*
*
* #param string $key
* #param string $value
*
* #return string
*/
public function decrypt($key, $value) {
return trim(openssl_decrypt(base64_decode(strtr($value, '-_,', '+/=')), 'aes-256-cbc', hash('sha256', $key, true)));
}
}
Line 23 is this: return strtr(base64_encode(openssl_encrypt($value, 'aes-256-cbc', hash('sha256', $key, true))), '+/=', '-_,');
Then I browsed around forums and found altered encryption.php that looks like this:
<?php
/**
* #package OpenCart
* #author Daniel Kerr
* #copyright Copyright (c) 2005 - 2017, OpenCart, Ltd. (https://www.opencart.com/)
* #license https://opensource.org/licenses/GPL-3.0
* #link https://www.opencart.com
*/
/**
* Encryption class
*/
final class Encryption {
/**
*
*
* #param string $key
* #param string $value
*
* #return string
*/
public function encrypt($key, $value) {
// Remove the base64 encoding from our key
$encryption_key = base64_decode($value);
// Generate an initialization vector
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-gcm'));
// Encrypt the data using AES 256 encryption in GCM mode using our encryption key and initialization vector.
$encrypted = openssl_encrypt($key, 'aes-256-gcm', $encryption_key, 0, $iv);
// The $iv is just as important as the key for decrypting, so save it with our encrypted data using a unique separator (::)
return base64_encode($encrypted . '::' . $iv);
}
/**
*
*
* #param string $key
* #param string $value
*
* #return string
*/
public function decrypt($key, $value) {
// Remove the base64 encoding from our key
$encryption_key = base64_decode($value);
// To decrypt, split the encrypted data from our IV - our unique separator used was "::"
list($encrypted_data, $iv) = explode('::', base64_decode($key), 2);
return openssl_decrypt($encrypted_data, 'aes-256-gcm', $encryption_key, 0, $iv);
}
}
When I use modified encryption.php file, I get the following warning:
PHP Warning: openssl_encrypt(): A tag should be provided when using AEAD mode in ...system/library/encryption.php on line 30
Line 30 is this: $encrypted = openssl_encrypt($key, 'aes-256-gcm', $encryption_key, 0, $iv);
Since my coding skills are limited, I don't know what else to do? To contact hosting company or Cardinity? Any help will be hugely appreciated. Thank you!
The cardinity plugin uses opencarts provided encrypt library, which is still using deprecated functions.
When using this there is a PHP warning that mess up ajax json responses. If you want to get around this issue.
set
error_reporting(0);
right before wherever there is a use of encrypt()
error_reporting(0);
$this->encryption->encrypt(...)
The last patch should automatically fix this
https://github.com/cardinity/cardinity-opencart/releases/tag/3.0.x-3dsv2-patch1

PHP Where to save OpenSSL encryption key? [duplicate]

This question already has answers here:
Where should I store an encryption key for php?
(5 answers)
Closed 6 years ago.
I'm using this php function to encrypt some strings.
openssl_encrypt();
To generate the encryption key I use
$encryption_key = openssl_random_pseudo_bytes(32);
I also know that this encryption key should be stored somewhere.
The problem is that I don't want to store it in my database, because it could be accessible for hackers.
Where could I also store my keys safely?
P.S. It makes any sense to store encrypted data and used keys in the same database
Here's an example encryption class I created in PHP. The encryption key is stored in this class, which can then be used to decrypt encrypted DB values. Hope this helps.
/**
* Provides basic encryption and decryption of strings and objects.
* Reasonable protection is provided, but you are still responsible
* for sanitizing the source strings or objects prior to use.
*/
class Encrypter {
/**
* This is the global encryption key for the site.
* The longer you make this key, the more secure the encryption
*/
const MASTER_KEY = 'my_amazing_key_of_death';
private $key;
private $cipher;
private $mode;
private $iv;
private $iv_size;
private $key_size;
private $block_size;
public function __construct() {
$this->key = self::MASTER_KEY
$this->cipher = MCRYPT_BLOWFISH;
$this->mode = MCRYPT_MODE_CBC;
$this->block_size = mcrypt_get_block_size($this->cipher);
$this->iv_size = mcrypt_get_iv_size($this->cipher, $this->mode);
$this->key_size = mcrypt_get_key_size($this->cipher, $this->mode);
$this->iv = mcrypt_create_iv($this->iv_size, MCRYPT_RAND);
/**
* if the calculated keysize is shorter than
* they key provided, trim the provided key
* to match its length
*/
if (strlen($this->key) > $this->key_size) {
$this->key = substr($this->key, 0, $this->key_size);
}
}
/**
* Static method alias for string encryption
* #param string $string The string to encrypt
* #return string The encrypted string
*/
public static function enc($string) {
$e = new self;
return $e->encrypt_string($string);
}
/**
* Static method alias for string decryption
* #param string $enc_string The previously encrypted string
* #return string The decrypted/original string
*/
public static function dec($enc_string) {
$e = new self;
return $e->decrypt_string($enc_string);
}
/**
* Encrypt a string
* #param string $string - string to encrypt
* #return string - encrypted string
*/
function encrypt_string($string) {
$enc = mcrypt_encrypt(
$this->cipher,
$this->key,
$string,
$this->mode,
$this->iv
);
$enc = base64_encode($this->iv . $enc);
/**
* replace potentially illegal chars
*/
$enc = strtr($enc, '+/=', '-_,');
/**
* remove unnecessary and ugly trailing commas
*/
$enc = strrev($enc);
if(substr($enc,0,1) == ',') $enc = substr($enc,1);
if(substr($enc,0,1) == ',') $enc = substr($enc,1);
$enc = strrev($enc);
return $enc;
}
/**
* Decrypt an encrypted string and return the original
* #param string $s The string previously encrypted with this class
* #return string The original unencrypted string
*/
function decrypt_string($s) {
$s = strtr($s, '-_,', '+/=');
$s = base64_decode($s);
$this->iv_size = mcrypt_get_iv_size($this->cipher, $this->mode);
$this->iv = substr($s, 0, $this->iv_size);
$data = substr($s, $this->iv_size);
/**
* supress warnings because they happen every time
* IV parameter must be as long as the block size
* yet this still works perfectly
*/
$decrypted = #mcrypt_decrypt($this->cipher, $this->key, $data, $this->mode, $this->iv);
return trim($decrypted);
}
/**
* Serialize an object into an encrypted string
* #throws Exception
* #param object $object
* #return string
*/
function encrypt_object($object) {
if(is_resource($object)) throw new Exception("Cannot encrypt objects of type 'resource'");
$ser = serialize($object);
$enc = base64_encode($ser);
return $this->encrypt_string($enc);
}
/**
* Unserialize an encrypted string back into an object
* #param string $enc
* #return object
*/
function decrypt_object($enc) {
$dec = $this->decrypt_string($enc);
$unenc = base64_decode($dec);
return unserialize($unenc);
}
}

How to replicate MySQL's aes-256-cbc in PHP

Using this article as a guide I was able to successfully replicate MySQL's aes-128-ecb in PHP:
final class Encryption
{
// The key
const KEY = '36F3D40A7A41A827968BE75A87D60950';
/**
* Encrypt a string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function encrypt($string)
{
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, self::getMySQLKey(self::KEY), self::getPaddedString($string), MCRYPT_MODE_ECB);
}
/**
* Decrypt a string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function decrypt($string)
{
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, self::getMySQLKey(self::KEY), $string, MCRYPT_MODE_ECB), "\x00..\x10");
}
/**
* Get MySQL key
*
* #access public
* #static
* #param string $key
* #return string
*/
public static function getMySQLKey($key)
{
// The new key
$new_key = str_repeat(chr(0), 16);
// Iterate over the key and XOR
for ($i = 0, $l = strlen($key); $i < $l; ++$i)
{
$new_key[$i % 16] = $new_key[$i % 16] ^ $key[$i];
}
// Return the new key
return $new_key;
}
/**
* Get padded string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function getPaddedString($string)
{
return str_pad($string, (16 * (floor(strlen($string) / 16) + 1)), chr(16 - (strlen($string) % 16)));
}
}
As an example:
// PHP, gives CJI+zJyviQI7GgSCLGMNsqsXq2MDKC3a9FIG3wDrE8Y=
base64_encode(Encryption::encrypt('michael#example.com'))
// MySQL, gives CJI+zJyviQI7GgSCLGMNsqsXq2MDKC3a9FIG3wDrE8Y=
SELECT TO_BASE64(AES_ENCRYPT('michael#example.com', '36F3D40A7A41A827968BE75A87D60950'));
However, I want to update to using aes-256-cbc but am having difficulties. I started by replacing MCRYPT_RIJNDAEL_128 with MCRYPT_RIJNDAEL_256 and MCRYPT_MODE_ECB with MCRYPT_MODE_CBC and used the KEY constant as the initialization vector:
/**
* Encrypt a string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function encrypt($string)
{
return mcrypt_encrypt(MCRYPT_RIJNDAEL_256, self::getMySQLKey(self::KEY), self::getPaddedString($string), MCRYPT_MODE_CBC, self::KEY);
}
/**
* Decrypt a string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function decrypt($string)
{
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, self::getMySQLKey(self::KEY), $string, MCRYPT_MODE_CBC, self::KEY), "\x00..\x10");
}
The problem is that I'm now getting different values from PHP and MySQL:
// PHP, gives XSRfnrl05CE7JIHCvfhq6D67O0mAW2ayrFv2YkjFVYI=
base64_encode(Encryption::encrypt('michael#example.com'))
// MySQL, gives lTLT4MRXcHnOAsYjlwUX4WVPHgYvyi6nKC4/3us/VF4=
SELECT TO_BASE64(AES_ENCRYPT('michael#example.com', '36F3D40A7A41A827968BE75A87D60950', '36F3D40A7A41A827968BE75A87D60950'));
I'm unsure where to proceed from here so any help would be appreciated.
And just to confirm that MySQL is indeed using the correct encryption method:
// aes-256-cbc
SELECT ##session.block_encryption_mode
AES is a subset of Rijndael so to use it for AES one must select a block size of 128-bits and a key size of 128, 192 or 256 bits.
MCRYPT_RIJNDAEL_256 specifies a block size of 256-bits, not the key size, similarly MCRYPT_RIJNDAEL_128 specifies a block size of 128-bits, not the key size. This is a common confusion.
This implementation of Rijndael auto selects a key size based on the passed key so it is important to use a key of exactly the correct desired size.
Also note that the iv length is the same as the block size so for AES it is 128-bits in length.
This is a good example of how poor naming can have unfortunate consequences.
As neither eggyal nor Artjom B. have chosen to provide an answer with their solution, I'll do so on their behalf.
The first issue is that AES-256 still uses MCRYPT_RIJNDAEL_128 rather than MCRYPT_RIJNDAEL_256, and the second is that AES-256 is used over AES-128 by providing a 32 byte key rather than a 16 byte key.
The below provides a correct implementation:
final class AESEncrypter
{
// The key
const KEY = 'F40E2A9E22150793C6D0CA9E316FEA42';
// The IV
const IV = '5C354934224F698E';
/**
* Encrypt a string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function encrypt($string)
{
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, self::getMySQLKey(self::KEY), self::getPaddedString($string), MCRYPT_MODE_CBC, self::IV);
}
/**
* Decrypt a string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function decrypt($string)
{
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, self::getMySQLKey(self::KEY), $string, MCRYPT_MODE_CBC, self::IV), "\x00..\x10");
}
/**
* Get MySQL key
*
* #access public
* #static
* #param string $key
* #return string
*/
public static function getMySQLKey($key)
{
// The new key
$new_key = str_repeat(chr(0), 32);
// Iterate over the key and XOR
for ($i = 0, $l = strlen($key); $i < $l; ++$i)
{
$new_key[$i % 32] = $new_key[$i % 32] ^ $key[$i];
}
// Return the new key
return $new_key;
}
/**
* Get padded string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function getPaddedString($string)
{
return str_pad($string, (16 * (floor(strlen($string) / 16) + 1)), chr(16 - (strlen($string) % 16)));
}
}
As an example:
// PHP, gives mwVraDh/7jG3BvPJyYqgxY6Ca8CTRN5JHvwPGeV8Vd0=
base64_encode(AESEncrypter::encrypt('michael#example.com'))
// MySQL, gives mwVraDh/7jG3BvPJyYqgxY6Ca8CTRN5JHvwPGeV8Vd0=
SELECT TO_BASE64(AES_ENCRYPT('michael#example.com', 'F40E2A9E22150793C6D0CA9E316FEA42', '5C354934224F698E'))

Rijndael 256-bit encryption/decryption throws an error

I've got the following class.
<?php
/**
* Cypher Class
*/
class Cipher
{
/**
* SHA256 Encrypted Key
* #var string
*/
private $encryptedKey;
/**
* Initial vector
*
* Used to seed the encryption string
*
* #var string
*/
private $initVector;
/**
* Constructor
* #param boolean|string $personalKey Holds the personal key to use in encryption
*/
public function __construct($personalKey = false)
{
// $config = configuration::getInstance();
if (false === $personalKey) {
return false;
} else {
$key = $personalKey;
}
$this->encryptionKey = hash('sha256', $key, TRUE);
$size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CFB);
$this->initVector = mcrypt_create_iv($size, MCRYPT_DEV_URANDOM);
}
/**
* Encrypt a string
* #param mixed $input Data to encrypt
* #return string Encrypted data
*/
public function encrypt($input)
{
return array(
'salt' => base64_encode($this->initVector),
'encrypted_value' => base64_encode(mcrypt_encrypt(
MCRYPT_RIJNDAEL_256,
$this->encryptionKey,
$input,
MCRYPT_MODE_CFB,
$this->initVector
)));
}
/**
* Decrypt string
* #param string $input Encrypted string we are going to decrypt
* #param string $salt Encrypted salt used to decrypt
* #return string Decrypted output
*/
public function decrypt($input, $salt)
{
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->encryptionKey, base64_decode($input), MCRYPT_MODE_CFB, $salt));
}
}
Both my IV and encrypted string are 16 bytes but for some reason PHP gives me the following error:
Warning: mcrypt_encrypt(): The IV parameter must be as long as the blocksize in C:\xampp\htdocs\public\cipher\Cipher.php on line 58
I am not really a star in encryptions and so I was wondering if one of you good people could help me out.
The size of an IV is related to the block size of the cipher. So whatever you choose as the block size must be used as the length of the IV.
Rijndael supports block sizes of 128, 160, 192, 224, and 256 bits. If you use MCRYPT_RIJNDAEL_128 then you are using the 128-bit block size (which matches AES). If you use MCRYPT_RIJNDAEL_256 then your block-size is twice as large.
So, you must decide which block size you want to use and ensure your code consistently refers to the same MCRYPT_RIJNDAEL_* constant.
I assume the website is giving you an error because it's expecting a 16-byte block size (as per AES).

Categories