I have an issue using mcrypt to encrypt a file on filesystem to e.g. store it into Mysql database. I have reduced the issue to the following lines of code:
<?php
$key = vzc_generateKey();
$file_content = file_get_contents("test.pdf"); // Fails
$file_content = file_get_contents("test2.docx"); // Fails
//$file_content = "12323"; // Works great
$hash_start = md5($file_content);
$encrypt = vzc_encryptV3($file_content, $key);
$decrypt = vzc_decryptV3($encrypt, $key);
$hash_end = md5($decrypt);
echo ($hash_end == $hash_start)."##";
function vzc_generateKey()
{
$cstrong = false;
while ($cstrong == false)
{
$bytes = openssl_random_pseudo_bytes(16, $cstrong);
}
return bin2hex($bytes);
}
function vzc_decryptV3($crypt,$key) {
$content = base64_decode($crypt['crypt']);
$iv = $crypt['iv'];
$rijndael = 'rijndael-256';
$cp = mcrypt_module_open($rijndael, '', 'ofb', '');
$ks = mcrypt_enc_get_key_size($cp);
$key = substr(md5($key), 0, $ks);
mcrypt_generic_init($cp, $key, $iv);
$decrypted = mdecrypt_generic($cp, $content);
mcrypt_generic_deinit($cp);
mcrypt_module_close($cp);
return trim(base64_decode($decrypted));
}
function vzc_encryptV3($file_content,$key) {
$content = base64_encode($file_content);
$rijndael = 'rijndael-256';
$cp = mcrypt_module_open($rijndael, '', 'ofb', '');
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($cp), MCRYPT_RAND);
else
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($cp), MCRYPT_DEV_RANDOM);
$ks = mcrypt_enc_get_key_size($cp);
$key = substr(md5($key), 0, $ks);
mcrypt_generic_init($cp, $key, $iv);
$encrypted = mcrypt_generic($cp, $content);
$returnvalue = array("crypt"=>trim(base64_encode($encrypted)), "iv"=>$iv);
mcrypt_generic_deinit($cp);
mcrypt_module_close($cp);
return $returnvalue;
}
?>
Using the String "12323" everything works fine, both Hashes do equal. But those two test files (one pdf and one docx) fail. It seems that the decryption returns different values then the origin data.
What can I do to solve this issue?
Thank you very much in advance for any tip you can provide.
It is probably the fact that the files are not exactly n * blocksize long. This leads the algorithm to pad the end of the file with '\0' and this changes the content of the file when you do the md5 calculation.
One way around this is to strip the padding off of the last block, if you can reliably find the end of the file.
Related
I have been trying to find out how to make this:
function encrypt_decrypt($action, $string) {
$output = false;
$encrypt_method = "AES-256-CBC";
$secret_key = 'HqFdkh2FX126fH1r';
$secret_iv = 'iS2dk82dXd26f61K';
// hash
$key = hash('sha256', $secret_key);
// iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
$iv = substr(hash('sha256', $secret_iv), 0, 16);
if ( $action == 'encrypt' ) {
$output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
$output = base64_encode($output);
} else if( $action == 'decrypt' ) {
$output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
}
return $output;
}
In Node.js
The reason is that the encryption will be handled by PHP and the decryption by Node.
EDIT:
I managed to get this far:
var crypto = require('crypto')
, key = 'cI8Jd96NDoasd09jcI8Jd96NDoasd09j'
, iv = 'cI8Jd96NDoasd09j'
, plaintext = '2';
hashedKey = crypto.createHash('sha256').update(key, 'utf-8').digest('hex');
console.log('hashed key=', hashedKey);
// corresponds to the hashed key in PHP
hashedIv = crypto.createHash('sha256').update(iv, 'utf-8').digest('hex').substring(0,16);
console.log('hashed iv=', hashedIv);
// corresponds to the hashed iv in PHP
var buf = Buffer.from(teamId, 'base64');
console.log("buffer: " + buf);
and the variable buf actually is the same as base64_decode($string in the PHP code.
However, when I do this:
var decipher = crypto.createDecipheriv("aes-256-cbc",key, iv);
var decrypted = decipher.update(buf, 'base64', 'utf8');
console.log("decrypted.toString(): " + decrypted.toString());
I'm getting Z���ߋd�M:�� in the console rather than the desired 2.
The main problem was an embarrasing one. We are mainly two devs on this project and I thought the php-file I was editing for the encryption and decryption was the only thing I had to care about.
It was later realized that the actual call for the encoding was made from another php-file. Thus, what I changed in the encoding in the file I was working on was all in vain.
The end result looks like this, for anyone who's interested:
function encrypt_decrypt($action, $string) {
$output = false;
$encrypt_method = "AES-256-CBC";
$secret_key = '32 byte key';
$secret_iv = '16 byte iv';
// hash
$key = substr(hash('sha256', $secret_key), 0, 32);
// iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
$iv = substr(hash('sha256', $secret_iv), 0, 16);
if ( $action == 'encrypt' ) {
$output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
//$output = base64_encode($output);
} else if( $action == 'decrypt' ) { // this below is now handled in Node
$output = openssl_decrypt($string, $encrypt_method, $key, 0, $iv);
//$output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
}
return $output;
}
And Node:
function getDecryptedTeamId(encryptedId) {
var hashedKey;
var hashedIv;
var crypto = require('crypto')
, key = 'same 32 byte key as above in php'
, iv = 'same 16 byte ivas above in php'
, plaintext = '2';
hashedKey = crypto.createHash('sha256').update(key, 'utf-8').digest('hex').substring(0,32);
key = hashedKey;
hashedIv = crypto.createHash('sha256').update(iv, 'utf-8').digest('hex').substring(0,16);
iv = hashedIv;
var buf = Buffer.from(encryptedId, 'base64');
var crypt = buf.toString('base64');
var decryptor = crypto.createDecipheriv("aes-256-cbc", hashedKey, hashedIv);
var teamIdDec = decryptor.update(buf);
teamIdDec += decryptor.final();
return teamIdDec;
}
I try to implement Crypt::encrypt function in php and this code is here:
$key = "ygXa6pBJOWSAClY/J6SSVTjvJpMIiPAENiTMjBrcOGw=";
$iv = random_bytes(16);
$value = \openssl_encrypt(serialize('123456'), 'AES-256-CBC', $key, 0, $iv);
bIv = base64_encode($iv);
$mac = hash_hmac('sha256', $bIv.$value, $key);
$c = ['iv'=>$bIv,'value'=>$value,'mac'=>$mac];
$json = json_encode($c);
$b = base64_encode($json);
But result is wrong.
I am thinking i should do something on $key before set in openssl_encrypt function.
Please help.
Thank you.
SOLVED:
We can implement this method like this:
$text = '123456';
$key = "ygXa6pBJOWSAClY/CFEdOTjvJpMIiPAMQiTMjBrcOGw=";
$key = (string)base64_decode($key);
$iv = random_bytes(16);
$value = \openssl_encrypt(serialize($text), 'AES-256-CBC', $key, 0, $iv);
$bIv = base64_encode($iv);
$mac = hash_hmac('sha256', $bIv.$value, $key);
$c_arr = ['iv'=>$bIv,'value'=>$value,'mac'=>$mac];
$json = json_encode($c_arr);
$crypted = base64_encode($json);
echo $crypted;
This work tor me.
enjoy :)
Be Successful
Here is the implementation, directly from the official source code.
public function encrypt($value)
{
$iv = random_bytes(16);
$value = \openssl_encrypt(serialize($value), $this->cipher, $this->key, 0, $iv);
if ($value === false) {
throw new EncryptException('Could not encrypt the data.');
}
// Once we have the encrypted value we will go ahead base64_encode the input
// vector and create the MAC for the encrypted value so we can verify its
// authenticity. Then, we'll JSON encode the data in a "payload" array.
$mac = $this->hash($iv = base64_encode($iv), $value);
$json = json_encode(compact('iv', 'value', 'mac'));
if (! is_string($json)) {
throw new EncryptException('Could not encrypt the data.');
}
return base64_encode($json);
}
$iv should be the same as in the source
$this->key is the encryption key you set in your .env file, encoded in b64
$this->cipher should be the one you configured in your laravel configurations and compatible to your key-length.
In your example, you have set your $key to the value after the "base64:"-string, which is not the key. You need to encode the key with base64 before passing it.
So the the $key to the base64 encode of ygXa6pBJOWSAClY/J6SSVTjvJpMIiPAENiTMjBrcOGw=, which is eWdYYTZwQkpPV1NBQ2xZL0o2U1NWVGp2SnBNSWlQQUVOaVRNakJyY09Hdz0K
I want to create and encryption for get variabile passed in url and for asynchronous call
for example:
$textToEncrypt = "Hello World";
$encryptionMethod = "AES-256-CBC";
$secretHash = "cVb67YtfAz328oOikl96vBn";
$iv = "adfrf54dmnlo09ax";
$encryptedText = openssl_encrypt($textToEncrypt,$encryptionMethod,$secretHash, 0, $iv);
result is: W2p0S2qlSierJnIcA/AM3g==
there are some special characters, == always at the end. I want to prevent this! How can I output only 0-9 and A-Z and a-z characters?
thanks
I had the same issue. I wanted to remove the special characters. So, this is what I did. Convert the encrypted text into hex value using base64_encode($encryptedText). So, there will be no special characters. Then for the revert, use base64_decode before passing to openssl_decrypt.
$ciphering = "AES-128-CTR";
use this as your cipher string, it'll remove any type of == from the end
I notice I had exactly 2 equal signs at the end of my encrypted string too. It seems theres always 2 equal signs at the end. Here's my solution
function encryptString($string, $action, $baseIP = 'false', $extraKey = ''){
global $flag;
$encryptedIP = '';
if($baseIP){
$encryptedIP = encryptString($_SERVER['REMOTE_ADDR'], 'encrypt', false);
}
$output = false;
$encrypt_method = "AES-256-CBC";
$secret_key = $flag['2nd-encrypt-key'].$encryptedIP.'-'.$extraKey;
$secret_iv = $flag['2nd-encrypt-secret'].$encryptedIP.'-'.$extraKey;
$key = hash('sha256', $secret_key);
$iv = substr(hash('sha256', $secret_iv), 0, 16);
$output;
if($action == 'encrypt'){
$output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
$output = base64_encode($output);
//replace equal signs with char that hopefully won't show up
$output = str_replace('=', '[equal]', $output);
}else if($action == 'decrypt'){
//put back equal signs where your custom var is
$setString = str_replace('[equal]', '=', $string);
$output = openssl_decrypt(base64_decode($setString), $encrypt_method, $key, 0, $iv);
}
return $output;
}
I want to encrypt/decrypt files by chunks, because file size can be quite large (50-100Mb). I found code of encryption class on stack overflow and changed it just a bit:
class filecrypt{
var $_CHUNK_SIZE;
var $_CHIPHER;
var $_MODE;
function __construct($chipher, $mode){
$this->_CHUNK_SIZE = 100*1024; // 100Kb
$this->_CHIPHER = $chipher;
$this->_MODE = $mode;
}
public function setChunkSize($value)
{
$this->_CHUNK_SIZE = $value;
}
public function encrypt($string, $key, $vector){
$key = pack('H*', $key);
if (extension_loaded('mcrypt') === true) return mcrypt_encrypt($this->_CHIPHER, substr($key, 0, mcrypt_get_key_size($this->_CHIPHER, $this->_MODE)), $string, $this->_MODE, $vector);
return false;
}
public function decrypt($string, $key, $vector){
$key = pack('H*', $key);
if (extension_loaded('mcrypt') === true) return mcrypt_decrypt($this->_CHIPHER, substr($key, 0, mcrypt_get_key_size($this->_CHIPHER, $this->_MODE)), $string, $this->_MODE, $vector);
return false;
}
public function encryptFileChunks($source, $destination, $key, $vector){
return $this->cryptFileChunks($source, $destination, $key, 'encrypt', $vector);
}
public function decryptFileChunks($source, $destination, $key, $vector){
return $this->cryptFileChunks($source, $destination, $key, 'decrypt', $vector);
}
private function cryptFileChunks($source, $destination, $key, $op, $vector){
if($op != "encrypt" and $op != "decrypt") return false;
$buffer = '';
$inHandle = fopen($source, 'rb');
$outHandle = fopen($destination, 'wb+');
if ($inHandle === false) return false;
if ($outHandle === false) return false;
while(!feof($inHandle)){
$buffer = fread($inHandle, $this->_CHUNK_SIZE);
if($op == "encrypt") $buffer = $this->encrypt($buffer, $key, $vector);
elseif($op == "decrypt") $buffer = $this->decrypt($buffer, $key, $vector);
fwrite($outHandle, $buffer);
}
fclose($inHandle);
fclose($outHandle);
return true;
}
public function printFileChunks($source, $key, $vector){
$buffer = '';
$inHandle = fopen($source, 'rb');
if ($inHandle === false) return false;
while(!feof($inHandle)){
$buffer = fread($inHandle, $this->_CHUNK_SIZE);
$buffer = $this->decrypt($buffer, $key, $vector);
echo $buffer;
}
return fclose($inHandle);
}
}
So, I tested this class in my script:
$chipher = MCRYPT_RIJNDAEL_128;
$mode = MCRYPT_MODE_CFB;
$filecrypt = new filecrypt($chipher, $mode);
$key = '3da541559918a808c2402bba5012f6c60b27661c'; // Your encryption key
$vectorSize = mcrypt_get_iv_size($chipher, $mode);
$vector = mcrypt_create_iv($vectorSize, MCRYPT_DEV_URANDOM);
//Encrypt file
$filecrypt->setChunkSize(8*1024);
$filecrypt->encryptFileChunks(APPLICATION_PATH.'/../data/resources/img1.jpg', APPLICATION_PATH.'/../data/resources/img1_en.jpg', $key, $vector);
//Decrypt file
$filecrypt->setChunkSize(8*1024);
$filecrypt->decryptFileChunks(APPLICATION_PATH.'/../data/resources/img1_en.jpg', APPLICATION_PATH.'/../data/resources/img1_res.jpg', $key, $vector);
Everything works fine and restores image is absolutely the same as source image.
BUT if I set different chunk size for encryption and decryption processes, restored image will be corrupted. This is source image:
This is restored image after encryption/decryption with different chunk size:
Here is a code:
$chipher = MCRYPT_RIJNDAEL_128;
$mode = MCRYPT_MODE_CFB;
$filecrypt = new filecrypt($chipher, $mode);
$key = '3da541559918a808c2402bba5012f6c60b27661c'; // Your encryption key
$vectorSize = mcrypt_get_iv_size($chipher, $mode);
$vector = mcrypt_create_iv($vectorSize, MCRYPT_DEV_URANDOM);
//Encrypt file
$filecrypt->setChunkSize(8*1024);
$filecrypt->encryptFileChunks(APPLICATION_PATH.'/../data/resources/img1.jpg', APPLICATION_PATH.'/../data/resources/img1_en.jpg', $key, $vector);
//Decrypt file
$filecrypt->setChunkSize(4*1024);
$filecrypt->decryptFileChunks(APPLICATION_PATH.'/../data/resources/img1_en.jpg', APPLICATION_PATH.'/../data/resources/img1_res.jpg', $key, $vector);
My question is how does chunk size influence on encryption/decryption process? Does it connected with block cipher mode and paddings? How can I encrypt data by chunks?
Maybe I should use another cipher for this purpose?
Does it connected with block cipher mode and paddings?
Spot on.
Maybe I should use another cipher for this purpose?
You could do that and use a stream-like cipher mode such as CTR, which doesn't require padding.
OR you could take care of the chunk sizes that you pick. It should be divisible by whatever mcrypt_get_block_size() returns for your cipher.
Basically, block cipher modes will split your data into "chunks" too, only those would be called blocks - hence, block size. Whenever the condition $dataSize % $blockSize !== 0 is true, the last block will be padded (by MCrypt) with NUL-bytes.
Then, during the decryption phase, MCrypt doesn't trim these NUL-bytes, and your image gets corrupted.
I have no idea what I'm doing wrong. I just need to be able to encrypt and decrypt without getting weird characters or warnings. It says I'm supposed to be using an IV of length 16 and that I'm using a length of 9 but "0123456789abcdef" is 16 characters.
Warning: mcrypt_generic_init() [function.mcrypt-generic-init]: Iv size incorrect; supplied length: 9, needed: 16 in /home/mcondiff/public_html/projects/enc/enc.php on line 10
See http://www.teamconcept.org/projects/enc/enc.php
I'm lost, confused, a little lightheaded. Here do I go from here? I have to use this encryption and get it working for a project.
<?php
class enc
{
function encrypt($str, $key) {
$key = $this->hex2bin($key);
$td = mcrypt_module_open("rijndael-128", "", "cbc", "fedcba9876543210");
mcrypt_generic_init($td, $key, CIPHER_IV);
$encrypted = mcrypt_generic($td, $str);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return bin2hex($encrypted);
}
function decrypt($code, $key) {
$key = $this->hex2bin($key);
$code = $this->hex2bin($code);
$td = mcrypt_module_open("rijndael-128", "", "cbc", "fedcba9876543210");
mcrypt_generic_init($td, $key, CIPHER_IV);
$decrypted = mdecrypt_generic($td, $code);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return utf8_encode(trim($decrypted));
}
function hex2bin($hexdata) {
$bindata = "";
for ($i = 0; $i < strlen($hexdata); $i += 2) {
$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
}
return $bindata;
}
}
$theEncryption = new enc();
$user = "John Doe";
$email = "john#example.com";
$user = $theEncryption->encrypt($user, "0123456789abcdef");
$email = $theEncryption->encrypt($email, "0123456789abcdef");
echo 'User: '.$user;
echo 'Email: '.$email;
?>
Can somone point me in the right direction or point out what i'm doing wrong?
Thanks
Mike
CIPHER_IV is probably an undefined constant. PHP raises a "Use of undefined constant" notice and then uses the "constant" as string. The string "CIPHER_IV" is 9 characters long.
In your php file, do a print of CIPHER_IV and see what it contains.
See http://us2.php.net/mcrypt_generic_init for the specifics
You've probably copy-pasted the code from a blog: googling mcrypt_generic_init CIPHER_IV only gives this post and a blog ;)
The IV is a parameter that you need to specify to the function, not a constant that the first blogger put in misinterpreting the second blogger's article.
At http://propaso.com/blog/?cat=6, they declare these:
$secret_key = "01234567890abcde";
$iv = "fedcba9876543210";
and then do:
mcrypt_generic_init($td, $secret_key, $iv);
Simply declare your IV to be something, then use it.