i have a little problem: my decryption won't give me the same string i encoded, and i can't find the problem... looked in other posts, but nothing helpful there
here are my functions:
public static function encryptData($data){
if($key = self::getEncryptionKey()){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_ECB,$iv));
} else {
return false;
}
}
public static function decryptData($data)
{
if($key = self::getEncryptionKey()){
$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, $key, base64_decode($data), MCRYPT_MODE_ECB,$iv);
} else {
return false;
}
}
where's the problem? i'm kinda desperate here...
To decrypt you need everything exactly the same at both sides. key, mode, IV and padding.
Looking at your code, you appear to be generating a new IV for decryption. Don't. Use the same IV as you used to encrypt.
You, correctly, specify a mode explicitly, but you pick the worst possible mode. Don't use ECB, it leaks information. Use CBC or CTR mode instead.
You don't specify a padding. Far better to specify it explicitly, use PKCS7 with Rijndael.
If none of that helps, then check your key, byte by byte, to make sure it is the same for both encryption and decryption.
Related
I'm using a function to encode and decode some text ,
But seems it doesn't support my country language (Persian) and change them to some unreadable text, How can i fix it .
<?php
class encrypt {
/********* Encode *********/
public static function encode($pure_string, $encryption_key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, md5(base64_encode(trim($encryption_key))), utf8_encode(trim($pure_string)), MCRYPT_MODE_ECB, $iv);
return base64_encode($encrypted_string);
}
/********** Decode ************ */
public static function decode($encrypted_string, $encryption_key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, md5(base64_encode(trim($encryption_key))),base64_decode(trim($encrypted_string)), MCRYPT_MODE_ECB, $iv);
return $decrypted_string;
}
}
?>
The same IV needs to be used for encryption and decryption, a general method is to prefix the encrypted data with the IV for use during decryption.
There is no need to Base64 encode or trim the encryption key.
Do not trim the encrypted data, encrypted data can contain null (0x00) bytes.
Encryption operates on bytes, not characters, as such any text language makes no difference.
Adding calls inline to other calls just makes debugging more difficult, separate the steps into separate statements with intermediate variables.
Do not use Blowfish, use AES, even the creator of Blowfish uses AES.
Do not use ECB mode, it is insecure, see ECB mode, scroll down to the Penguin.
ECB mode does not use an IV.
What you are doing is encryption, not encoding.
Consider using defuse or RNCryptor, they provide a complete solution and are being maintained.
I am implementing a reversible encryption algorithm based on an example at http://www.edzynda.com/create-a-self-destructing-encrypted-message-app-in-laravel-part-1/
I have put the code inside a model event function:
public static function boot()
{
parent::boot();
// Setup event bindings...
static::creating(function($account)
{
/* encrypt password and save iv */
$encryption = self::encrypt($account->db_password); // or: Input::get('db_password')
$account->db_password = $encryption['encrypted_string'];
$account->iv = $encryption['iv'];
Log::debug($account);
//print_r ($encryption);
});
}
public static function encrypt($string)
{
// Generate an IV
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CFB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);
// Encrypt the string
$encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $_ENV['ACCOUNT_KEY'], $string, MCRYPT_MODE_CFB, $iv);
return ['encrypted_string' => $encrypted_string, 'iv' => $iv];
}
In my database seeder, I am calling Model::create to make the string is encrypted before saving. This is called from my tests. However, I am getting
[ErrorException]
json_encode(): Invalid UTF-8 sequence in argument
I've made sure that the column type is binary.
Extra info: when I perform mb_detect_encoding on either value ($iv or $string) I get either UTF-8, or nothing, I guess depending not the random characters that appear in the result.
My solution: for storage and transportation, the encrypted string and IV need to be base64 encoded.
The relevant line above changed to:
return [
'encrypted_string' => base64_encode($encrypted_string),
'iv' => base64_encode($iv)
];
and of course base64_decode must be used before decrypting the string with the stored IV value.
I'm using PEAR::Mail to send a lot of e-mails to our customers. I want to be able to send those e-mails using different SMTP accounts (because of different mailing types). We have like 6-7 accounts, and in the future there might be more. Each of them has different password and we want to be able to store those passwords in database so it's not hardcoded and so you can add them more easily with administrator panel.
I know I want to use encryption for password storing, but imo hashing is not an option here. I want to be able to read those passwords, not only compare the hashes.
I would like to do it with storing encrypted passwords in database but encrypt them with some algorithm. And that's where I have problem - I don't know much about that. I'm attaching my test code for encryption, but I would like your opinion on how should I improve it:
if (!function_exists('hex2bin')) {
function hex2bin($data) {
$len = strlen($data);
return pack('H' . $len, $data);
}
}
$key = $_GET['key'];
$text = $_GET['text'];
$encr = $_GET['encr'];
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
if ($text != null) {
echo bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv));
}
if ($encr != null) {
echo mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, hex2bin($encr), MCRYPT_MODE_ECB);
}
ECB mode is insecure and the IV is ignored with this mode. You should really use CBC (MCRYPT_MODE_CBC) instead.
When using CBC an IV is required for encryption and the same IV is required for decryption, so you need to hang on to this value (but don't use the same IV for all encryption/decryption, generating a random one as in your code example is correct). The IV does not need to be stored securely (any more securely than the encrypted data), and it's standard proceedure to prepend the IV to the encrypted data.
bin2hex($iv . mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv));
When decrypting you strip off the IV and pass it in to mcrypt_decrypt.
$cipherTextDecoded = hex2bin($encr);
$iv = substr($cipherTextDecoded, 0, $iv_size);
$cipherText = substr($cipherTextDecoded, $iv_size);
mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $cipherText, MCRYPT_MODE_CBC, $iv);
Also note that you should be using a binary key. $_GET['key'] is returning a string of text, and because you're not hex decoding it your keyspace is limited to all possible 256-bit strings rather than all 256-bit binary values.
Further, it's a bit misleading in PHP, but the 256 in MCRYPT_RIJNDAEL_256 refers to the block size, not the strength of the encryption. If you want to use 256 bit encryption, just pass a 256 bit key to the mcrypt functions. If that was your goal I'd consider using MCRYPT_RIJNDAEL_128 instead, this will make the encrypted text compatible with AES-128. If you ever need to decrypt the data in some other system (unlikely I know), it's much easier to find an AES-128 imeplementation than Rijindael 256.
I created a class which does everything what Syon mentioned. I'm attaching it here for future reference, if anyone would like to use it then feel free.
<?php
if (!function_exists('hex2bin')) {
function hex2bin($data) {
$len = strlen($data);
return pack('H' . $len, $data);
}
}
/**
* Encipherer - Class used for encoding and decoding
*
* #author kelu
* #version $Id$
*
*/
class Encipherer {
private $key;
private $iv_size;
private $mode = MCRYPT_MODE_CBC;
private $algorithm = MCRYPT_RIJNDAEL_256;
private $rand = MCRYPT_RAND;
/**
* returns singleton
*
* #return Encipherer
*/
public static function Instance()
{
static $inst = null;
if ($inst === null) {
$inst = new Encipherer;
}
return $inst;
}
private function __construct($key = '') {
$this->iv_size = mcrypt_get_iv_size($this->algorithm, $this->mode);
$this->key = $this->key = hex2bin($key);
}
private function __clone()
{
return Encipherer::Instance();
}
public function setKey($key) {
$this->key = $this->key = hex2bin($key);
}
public function encrypt($text) {
$iv = mcrypt_create_iv($this->iv_size, $this->rand);
return bin2hex($iv . mcrypt_encrypt($this->algorithm, $this->key, $text, $this->mode, $iv));
}
public function decrypt($text) {
$cipherTextDecoded = hex2bin($text);
$iv = substr($cipherTextDecoded, 0, $this->iv_size);
$cipherText = substr($cipherTextDecoded, $this->iv_size);
return mcrypt_decrypt($this->algorithm, $this->key, $cipherText, $this->mode, $iv);
}
}
?>
Example usage:
<?
$enc = Encipherer::Instance();
$enc->setKey('1234qwerty');
$encrypted = $enc->encrypt('secret message');
$decrypted = $enc->decrypt($encrypted);
?>
These days I read a lot here on SO about password hashing and data encryption. It's a real mess, I mean, deciding what the best practice is. I need a very simple class that can be reused anywhere and that provide a decent-but-not-paranoic security level for my PHP applications (I do not handle bank data). Additionally, I want to rely as much as possible on PHP standard libs. I came up with this:
class Security {
public static function hashPassword($plain) {
$salt = md5(rand(0, 1023) . '#' . time()); // Random salt
return crypt($plain, '$2a$07$' . $salt); // '$2a$07$' is the Blowfish trigger
}
public static function checkPassword($plain, $hash) {
return (crypt($plain, $hash) === $hash);
}
public static function generateIv() {
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC); // It's 32
return mcrypt_create_iv($iv_size, MCRYPT_RAND);
}
public static function encrypt($key, $data, $iv = null, $base64 = true) {
if (is_null($iv)) $iv = md5($key);
$ret = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv);
return ($base64 ? base64_encode($ret) : $ret);
}
public static function decrypt($key, $data, $iv = null, $base64 = true) {
if (is_null($iv)) $iv = md5($key);
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $base64 ? base64_decode($data) : $data, MCRYPT_MODE_CBC, $iv), "\0");
}
}
As you can see, I choose to hash passwords with crypt() using Blowfish hashing algorithm. The return value of hashPassword() is the salt + hash that then I store in the DB. I made this choice because crypt() is available on every server, provides a confortable way to check hash regardless of algorithm used (it's based on salt prefix) and, I read, bcrypt is a decent hashing method.
Then, for data encryption I used mcrypt() Rijndael 256 algorithm with CBC mode. As you can see, I can use encryption methods in two way. I can pass a IV (and generateIv() helps me to create one) that I will store in the DB along crypted data, or, if I don't, a basic IV is derived from key in both crypt and decrypt process.
What do you think about it? Am I missing something? Can I be finally relaxed about hashing and encryption in my PHP aplications?!?
You are using Rijndael 256 bit encryption, which is not AES standard. Try to use AES (MCRYPT_RIJNDAEL_128) using 256 bit keys instead.
A random IV should be kept with cipher text if the derived key is also used to encrypt other data.
You are using out of date functions, you might want to use bcrypt and SHA-256 for the IV (only use the 16 - blocksize - left most bytes) .
Note that this list may not be complete.
I have a problem with CBC mode when I try to encrypt/decrypt some text using php's mcrypt extension. I've created a class to perform this operations, it works fine with other modes but CBC.
The problem is as follow:
I use the clear text Even in cryptography, silence is golden. I do the encryption part, no problem till this point. But each time I try to decrypt, I get something like this: 9��'t"�cryptography, silence is golden. As you can see, the first 8 characters of the text are wrong. I don't know what may be causing this behavior.
The parts of my class which handle these operations are:
public function encrypt($data)
{
$cypher = $this->_getCypher();
$iv = $this->_getIv($cypher);
return trim(base64_encode(mcrypt_encrypt($cypher, self::KEY, $data, MCRYPT_MODE_CBC, $iv)));
}
public function decrypt($data)
{
$cypher = $this->_getCypher();
$iv = $this->_getIv($cypher);
return trim(mcrypt_decrypt($cypher, self::KEY, base64_decode($data), MCRYPT_MODE_CBC, $iv));
}
protected function _getCypher()
{
return self::$_cyphers[$this->_algorithm];
}
protected function _getIv($cypher)
{
return mcrypt_create_iv(mcrypt_get_iv_size($cypher, MCRYPT_MODE_CBC), MCRYPT_RAND);
}
And the algorithm used for above example is 3DES. As I said before, using other mode, such as ECB, everything works fine.
Any suggestions ?
You need to remember the IV that you used for encryption and use that again for decryption.