I'm writing a class to handle encrypted data, essentially it will be used to encrypt data to be stored in a DB and then again to decrypt it on retrieval.
Here's what I've written:
class dataEncrypt {
private $encryptString;
private $decryptString;
private $encryptionMethod;
private $key;
public function __construct() {
/* IMPORTANT - DONT CHANGE OR DATA WILL DAMAGE */
$this->key = sha1('StringToHash');
// Set the encryption type
$this->encryptionMethod = "AES-256-CBC";
}
// Generate the IV key
private function generateIV() {
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
return mcrypt_create_iv($ivSize, MCRYPT_RAND);
}
// Retrieve the key
private function retrieveKey() {
return $key;
}
// Encrypt a string
public function encryptString($string) {
// Return the encrypted value for storage
return openssl_encrypt($string, $this->encryptionMethod, $this->retrieveKey(), 0, $this->generateIV());
}
// Decrypt a string
public function decryptString($data) {
// return the decrypted data
return openssl_decrypt($data, $this->encryptionMethod, $this->retrieveKey(), 0, $this->generateIV());
return false;
}
}
I'm trying to encrypt a string before storing, and I get the following PHP warning:
Warning: openssl_encrypt(): IV passed is 32 bytes long which is longer than the 16 expected by selected cipher, truncating in /var/www/blahblah... on line xxx
I've googled this, Ive googled the IV functions, I can't find sweetheat on either. Any advice is welcomed here.
Thanks
I was able to get it working by passing MCRYPT_CAST_256 rather than MCRYPT_RIJNDAEL_256 into mcrypt_get_iv_size
Encrypt:
$iv_size = mcrypt_get_iv_size(MCRYPT_CAST_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$encrypted = openssl_encrypt($string, "AES-256-CBC", $key, 0, $iv);
$encrypted = $iv.$encrypted;
Decrypt
$iv_size = mcrypt_get_iv_size(MCRYPT_CAST_256, MCRYPT_MODE_CBC);
$iv = substr($string, 0, $iv_size);
$decrypted = openssl_decrypt(substr($string, $iv_size), "AES-256-CBC", $key, 0, $iv);
Related
m using
public function encrypt($plain_str,$key)
{
$str= mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plain_str, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND));
$str = urlencode(base64_encode($str));
return $str ;
}
public function decrypt($cipher_str,$key)
{
$str = urldecode(base64_decode($cipher_str));
return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND));
}
on crypting :201433~58~g#fds.com~20140820142427
i get : %2BAihYMLwpwrsmL4lSGGzwFTfonvdCyOb%2BCGEUJ%2F%2BE%2F7ZnvgwFRYFtlazQeSrVjUjyaaGZADK8%2BZyynIGxyt4VQ%3D%3D
on decrypting : %2BAihYMLwpwrsmL4lSGGzwFTfonvdCyOb%2BCGEUJ%2F%2BE%2F7ZnvgwFRYFtlazQeSrVjUjyaaGZADK8%2BZyynIGxyt4VQ%3D%3D
i get :201433~58~g#fds.com~20140820142427 back but
when string is malformed like some character removed
like this : %2BAihYMLwpwrsmL4lSGGzwFTfonvdCyOb%2BCGEUJ%2F%2BE%2F7Z
on decrypting i get : 201433~58~g#fds.com~201408201424O#¿W«Gݽˋ¯ È#'oP´ŸØw\Â⦑
How can i detect this anomoly ?
First of all, I'd like to list some flaws in your code:
Don't use ECB mode.
You are encrypting using MCRYPT_RIJNDAEL_128, but you're getting the IV size for MCRYPT_RIJNDAEL_256. (btw, IV is ignored in ECB mode, which is one of the reasons why not to use it)
You are also using MCRYPT_RAND as your randomness source, which is not secure. You should use MCRYPT_DEV_URANDOM (that is also the new default in PHP 5.6).
You don't have to urlencode() the resulting ciphertext, Base64 encoding is URL-safe.
Now, to answer your question ... this is done via a HMAC. The easiest way to use a HMAC is to prepend the cipher-text with it (which you should do with the IV as well; don't worry, it's not a secret):
public function encrypt($plainText, $encKey, $hmacKey)
{
$ivSize = mcrypt_get_iv_size('rijndael-128', 'ctr');
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
$cipherText = mcrypt_encrypt('rijndael-128', $encKey, $plainText, 'ctr', $iv);
$cipherText = $iv.$cipherText;
$hmac = hash_hmac('sha256', $cipherText, $hmacKey, true);
return base64_encode($hmac.$cipherText);
}
public function decrypt($cipherText, $encKey, $hmacKey)
{
$cipherText = base64_decode($cipherText);
if (strlen($cipherText) <= 32)
{
throw new Exception('Authentication failed!');
}
$recvHmac = substr($cipherText, 0, 32);
$cipherText = substr($cipherText, 32);
$calcHmac = hash_hmac('sha256', $cipherText, $hmacKey, true);
if ( ! hash_equals($recvHmac, $calcHmac))
{
throw new Exception('Authentication failed!');
}
$ivSize = mcrypt_get_iv_size('rijndael-128', 'ctr');
$iv = substr($cipherText, $ivSize);
$cipherText = substr($cipherText, $ivSize);
return mcrypt_decrypt('rijndael-128', $encKey, $cipherText, 'ctr', $iv);
}
Please note that the encryption key and HMAC key are different - they most NOT be the same key. Also, for Rijndael-128, you should create a 128-bit (or 16-byte) random key, it is not something that you can just type in with your keyboard. Here's how to generate one:
$encKey = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
i want to decrypt magento data using the data encrypted and config key to show the data as plan test
i tried alotof ways but no one has done with me is there any way
i mean is there any way as php script to do it
and thanks
i used this code i found here but doesn't show anything
<?php
class Encryption
{
const CIPHER = MCRYPT_RIJNDAEL_128; // Rijndael-128 is AES
const MODE = MCRYPT_MODE_CBC;
/* Cryptographic key of length 16, 24 or 32. NOT a password! */
private $key;
public function __construct($key) {
$this->key = $key;
}
public function encrypt($plaintext) {
$ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_RANDOM);
$ciphertext = mcrypt_encrypt(self::CIPHER, $this->key, $plaintext, self::MODE, $iv);
return base64_encode($iv.$ciphertext);
}
public function decrypt($ciphertext) {
$ciphertext = base64_decode($ciphertext);
$ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
if (strlen($ciphertext) < $ivSize) {
throw new Exception('Missing initialization vector');
}
$iv = substr($ciphertext, 0, $ivSize);
$ciphertext = substr($ciphertext, $ivSize);
$plaintext = mcrypt_decrypt(self::CIPHER, $this->key, $ciphertext, self::MODE, $iv);
return rtrim($plaintext, "\0");
}
}
In simplest case, when we use all of standard Magento settings:
Encryptor get from model core/encryption,
Key from setting global/crypt/key
Using Mcrypt
With standard cipher MCRYPT_BLOWFISH and mode MCRYPT_MODE_ECB
(all given for Magento 1.8.1)
$encrypted = 'R4VQyYn6JHs=';
$key = '370ee4d319aebb395b982d72190588d2';
$cipher = MCRYPT_BLOWFISH;
$mode = MCRYPT_MODE_ECB;
$handler = mcrypt_module_open($cipher, '', $mode, '');
$initVector = mcrypt_create_iv (mcrypt_enc_get_iv_size($handler), MCRYPT_RAND);
mcrypt_generic_init($handler, $key, $initVector);
var_dump(str_replace("\x0", '', trim(mdecrypt_generic($handler, base64_decode($encrypted)))));
However, I can't see a point in using this, since you can use Magento, and just call
Magento::helper('core')->decrypt($encrypted);
Trying to achieve encrypting and decryption using following strategy, but ending up with random characters mostly.
class Crypt {
public static function encrypt($string, $account) {
// create a random initialization vector to use with CBC encoding
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = pack('H*', $account . $account);
$output = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $iv);
$output = $iv . $output;
$output = base64_encode($output);
$output = urlencode($output);
return $output;
}
public static function decrypt($token, $account) {
$ciphertext_dec = base64_decode($token);
// retrieves the IV, iv_size should be created using mcrypt_get_iv_size()
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv_dec = substr($ciphertext_dec, 0, $iv_size);
// retrieves the cipher text (everything except the $iv_size in the front)
$ciphertext_dec = substr($ciphertext_dec, $iv_size);
$key = pack('H*', $account . $account);
$token = urldecode($token);
$output = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);
$output = rtrim($output, "");
return $output;
}
}
Can't get exact values back, sometimes it decrypts but I see some garbage values, but mostly just random characters.
$a = \Crypt::encrypt("MyPassword", "1974246e");
echo \Crypt::decrypt($a, "1974246e");
Edits after the discussion
class Crypt {
public static function encrypt($data, $passphrase) {
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC); //create a random initialization vector to use with CBC encoding
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = pack('H*', $passphrase . $passphrase);
return base64_encode($iv . mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv));
}
public static function decrypt($data, $passphrase) {
$data = base64_decode($data);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC); //retrieves the IV, iv_size should be created using mcrypt_get_iv_size()
$iv = substr($data, 0, $iv_size);
$data = substr($data, $iv_size); //retrieves the cipher text (everything except the $iv_size in the front)
$key = pack('H*', $passphrase . $passphrase);
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv), chr(0));
}
}
Usage:
$pass = "MyPassword*&^*&^(*&^(";
$token = \Crypt::encrypt($pass, "1974246e8e8a479bb0233495e8a3ed12");
$answer = \Crypt::decrypt($token, "1974246e8e8a479bb0233495e8a3ed12");
echo $answer == $pass ? "yes" : "no";
Don't urlencode. Unnecessary.
trim for NULL bytes, not empty strings: rtrim($str, chr(0)); (Instead, you might want to save the source string length in the encrypted result too, so you won't rtrim() too much.)
Why pack('H*', $account) for $key? Also unnecessary.
Rijndael 128 uses 16 byte keys (128 bits), so make sure your key is at least that long:
$key = $account . $account
will do, but it obviously imperfect. (mcrypt will do something like that if it's too short.) If every account had its own passphrase, that would be good. (Even more so in combination with an app secret, but details.)
rtrim() with chr(0) is fine, very probably, because your source string won't have trailing NUL bytes.
I usually use these en/decrypt functions, or alike, but these have a static secret/key, so yours is better.
To send an encrypted token to the client:
$enc_token = Crypt::encrypt($token, $key);
// $enc_token might contain `/` and `+` and `=`
$url = 'page.php?token=' . urlencode($enc_token);
I found out that I should not use global variables like global $auth_key for sensitive data's (Correct me if that's not true.) so I wanted to use defined variables for storing security keys.
Inside config.php salt keys are defined.
define('AUTH_KEY','::~K~UC*[tlu4Eq/]Lm|h');
define('SECURE_AUTH_KEY', 'QsTMvbV+tuU{K26!]J2');
In encryption.php contains the encryption functions where AUTH_KEY and SECURE_AUTH_KEY will be used inside.
function encrypt_text($value) {
if(!$value) return false;
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, **AUTH_KEY_HERE**, $value, MCRYPT_MODE_ECB, **SECURE_AUTH_KEY_HERE**);
return trim(base64_encode($crypttext));
}
function decrypt_text($value) {
if(!$value) return false;
$crypttext = base64_decode($value);
$decrypttext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, **AUTH_KEY_HERE**, $crypttext, MCRYPT_MODE_ECB, **SECURE_AUTH_KEY_HERE**);
return trim($decrypttext);
}
Is there a way to do that? or any other solutions you can recommend? Please note that these keys are real important for encryption of sensitive informations.
Also, a another question, what is the maximum length of keys to be used on mcrypt?
Thank you and looking forward for reply of yours.
as a rule: the logner the key, the stonger the encryption. Secondly, don't use ECB unless your data is very short, you ought to use CBC or something stronger. Third: use a salt or initialization vector. Lastly read this: https://www.owasp.org/index.php/Cryptographic_Storage_Cheat_Sheet
Using a constant is just like using a variable except there is no dollar sign.
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, AUTH_KEY, $value, MCRYPT_MODE_ECB, SECURE_AUTH);
There is nothing inherently more secure in this approach over using the global key word. Though this approach is preferred. By using a constant you are saying this is a static value I will use across the application. Having to use global on the other hand is often just a result of bad design or laziness. It leads to code that is hard to follow, abusing what scoping tries to accomplish.
Key length is dependent on the encryption algorithm used. RTM.
Yes you can use the define variable like you are doing, see the example
define('AUTH_KEY','::~K~UC*[tlu4Eq/]Lm|h');
function abc()
{
echo AUTH_KEY;
}
abc(); // ::~K~UC*[tlu4Eq/]Lm|h
http://codepad.viper-7.com/tUAg6D
Although choosing constants would be preferable over plain variables, this kind of information is better stored inside a configuration file rather than your code.
Also, for better reuse and avoid having those global values lying around it would be a better idea to encapsulate the functionality:
class MyCrypto
{
private $key;
private $cipher;
private $mode;
public function __construct($key, $cipher, $mode = "cbc")
{
$this->key = $key;
$this->cipher = $cipher;
$this->mode = $mode;
}
public function generate_salt()
{
return mcrypt_create_iv(
mcrypt_get_iv_size($this->cipher, $this->mode),
MCRYPT_DEV_URANDOM
);
}
public function encrypt($data) { ... }
public function decrypt($data) { ... }
}
I've added a salt generator function to be used for every encryption operation;
Lastly, I would recommend using CBC mode - MCRYPT_MODE_CBC.
Update (27/09/17):
Since mcrypt_encrypt is DEPRECATED as of PHP 7.1.0. Ive added a simple encrypt/decrypt using openssl.
function encrypt($string, $key = 'PrivateKey', $secret = 'SecretKey', $method = 'AES-256-CBC') {
// hash
$key = hash('sha256', $key);
// create iv - encrypt method AES-256-CBC expects 16 bytes
$iv = substr(hash('sha256', $secret), 0, 16);
// encrypt
$output = openssl_encrypt($string, $method, $key, 0, $iv);
// encode
return base64_encode($output);
}
function decrypt($string, $key = 'PrivateKey', $secret = 'SecretKey', $method = 'AES-256-CBC') {
// hash
$key = hash('sha256', $key);
// create iv - encrypt method AES-256-CBC expects 16 bytes
$iv = substr(hash('sha256', $secret), 0, 16);
// decode
$string = base64_decode($string);
// decrypt
return openssl_decrypt($string, $method, $key, 0, $iv);
}
$str = 'Encrypt this text';
echo "Plain: " .$str. "\n";
// encrypt
$encrypted_str = encrypt($str);
echo "Encrypted: " .$encrypted_str. "\n";
// decrypt
$decrypted_str = decrypt($encrypted_str);
echo "Decrypted: " .$decrypted_str. "\n";
In your example, you are using the same initialization vector **SECURE_AUTH_KEY_HERE** when you can allow PHP to create the iv for you this way you only need 1 SECURE_KEY defined.
<?php
define('SECURE_KEY',md5('your secret key'));
/**
* Encrypt a value
*/
function encrypt($str){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
return mcrypt_encrypt(MCRYPT_RIJNDAEL_256, SECURE_KEY, $str, MCRYPT_MODE_ECB, $iv);
}
/**
* Decrypt a value
*/
function decrypt($str){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, SECURE_KEY, $str, MCRYPT_MODE_ECB, $iv));
}
//32
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
//Create an initialization vector (IV) from a random source
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
echo decrypt(encrypt('Encrypt me'));
?>
Here is my encode url param aKVXt4_P78X64w5ApVAZJ0fSNpV_GGFWxBs0aE_xw_24ghq1C5awAAPPKYbZSi0rGJUmyPlohVsP0fE4-jHQnN
When Encryption class decode it the result is ±�ыП^ґрьI§эЁ¶шЪ™МkVЃ°(ѓ7m‰e+и *“V«;Ё#ЧB§Z{Ћ‹JЈи_ЈWfUѕe
I don't understand the reason of such issue, it happens rather rarely but brings some trouble.
To uncode/decode links i use such class.
class Encryption {
// config local ENCRIPTION_KEY
var $skey = ENCRIPTION_KEY;
private function safe_b64encode($string) {
$data = base64_encode($string);
$data = str_replace(array('+','/','='),array('-','_',''),$data);
return $data;
}
private function safe_b64decode($string) {
$data = str_replace(array('-','_'),array('+','/'),$string);
$mod4 = strlen($data) % 4;
if ($mod4) {
$data .= substr('====', $mod4);
}
return base64_decode($data);
}
public function encode($value){
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_RAND);
$crypttext = trim(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->skey, trim($text), MCRYPT_MODE_ECB, $iv));
return trim($this->safe_b64encode($crypttext));
}
public function decode($value){
if(!$value){return false;}
$crypttext = $this->safe_b64decode($value);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypttext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->skey, $crypttext, MCRYPT_MODE_ECB, $iv);
return trim($decrypttext);
}
}
I believe the problem you are having is that you are encrypting using a random IV and then trying to decrypt using a completely different random IV. It is important to have a random IV, but you need the same IV to decrypt. The IV does not need to be secret as long as the constant KEY is hidden.
Also #Daniel was correct when saying that ECB doesn't use an IV. It will ignore it, so the last paragraph is now mute. However, you should consider switching from ECB to CBC as it is more secure (simply because it does use an IV).
I wrote a small class to encrypt/decrypt using MCRYPT in CBC mode (allowing any supported encryption algorithm e.g., BLOWFISH, TWOFISH, RIJNDAEL, etc). When encrypting it creates a random IV then prepends it to the encrypted string before returning the whole lot in hex. Then when it needs to decrypt the same string it will convert back to bin, determine the iv size of the encryption algorithm, remove the IV from the encrypted string and use it to return the decrypted string. If you think it might help, you can check it out. Hope it helps.