I am new to AES but from what I have found there are several modes (ECB,CBC, etc.) and different modes need different initialization vector requirements, blocks, and encodings. I am trying to decode the following
Xrb9YtT7cHUdpHYIvEWeJIAbkxWUtCNcjdzOMgyxJzU/vW9xHivdEDFKeszC93B6MMkhctR35e+YkmYI5ejMf5ofNxaiQcZbf3OBBsngfWUZxfvnrE2u1lD5+R6cn88vk4+mwEs3WoAht1CAkjr7P+fRIaCTckWLaF9ZAgo1/rvYA8EGDc+uXgWv9KvYpDDsCd1JStrD96IACN3DNuO28lVOsKrhcEWhDjAx+yh72wM=
using php and the (text) key "043j9fmd38jrr4dnej3FD11111111111" with mode CBC and an IV of all zeros. I am able to get it to work with this tool but can't get it in php. Here is the code I am using:
function decrypt_data($data, $iv, $key) {
$data = base64_decode($data);
$cypher = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, '');
// initialize encryption handle
if (mcrypt_generic_init($cypher, $key, $iv) != -1) {
// decrypt
$decrypted = mdecrypt_generic($cypher, $data);
// clean up
mcrypt_generic_deinit($cypher);
mcrypt_module_close($cypher);
return $decrypted;
}
return false;
}
I think I may be missing something relating to base 64 encoding or turning the key into binary first. I have tried decoding many things and all I can produce is gibberish. Any help would be very appreciated.
Well the tool itself does not say how exactly it's encrypted. And you can't set the IV either so it's hard to get the parameters right (because they have to be equal).
After some guesswork I found out the following:
The IV is prepended to the ciphertext
The ciphertext is encrypted with aes-128-cbc
So you have to modify the code:
function decrypt_data($data, $iv, $key) {
$cypher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
if(is_null($iv)) {
$ivlen = mcrypt_enc_get_iv_size($cypher);
$iv = substr($data, 0, $ivlen);
$data = substr($data, $ivlen);
}
// initialize encryption handle
if (mcrypt_generic_init($cypher, $key, $iv) != -1) {
// decrypt
$decrypted = mdecrypt_generic($cypher, $data);
// clean up
mcrypt_generic_deinit($cypher);
mcrypt_module_close($cypher);
return $decrypted;
}
return false;
}
$ctext = "Xrb9YtT7cHUdpHYIvEWeJIAbkxWUtCNcjdzOMgyxJzU/vW9x" .
"HivdEDFKeszC93B6MMkhctR35e+YkmYI5ejMf5ofNxaiQcZb" .
"f3OBBsngfWUZxfvnrE2u1lD5+R6cn88vk4+mwEs3WoAht1CA" .
"kjr7P+fRIaCTckWLaF9ZAgo1/rvYA8EGDc+uXgWv9KvYpDDs" .
"Cd1JStrD96IACN3DNuO28lVOsKrhcEWhDjAx+yh72wM=";
$key = "043j9fmd38jrr4dnej3FD11111111111";
$res = decrypt_data(base64_decode($ctext), null, $key);
I'm not sure why the key length is not used to encrypt it with aes-256-cbc - I've checked out the source of that as3crypto-library and it kind of supported it, but I would have to debug it to really verify it.
Related
I have a PHP page that loops through a CSV file and encrypts the 'email' column using the following function:
function my_encrypt($data, $key)
{
// Remove the base64 encoding from our key
$encryption_key = base64_decode($key);
// Generate an initialization vector
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
// Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
$encrypted = openssl_encrypt($data, 'aes-256-cbc', $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);
}
In another part of the app, I decrypt the returned value using:
function my_decrypt($data, $key)
{
// Remove the base64 encoding from our key
$encryption_key = base64_decode($key);
// To decrypt, split the encrypted data from our IV - our unique separator used was "::"
list($encrypted_data, $iv) = explode('::', base64_decode($data), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $encryption_key, 0, $iv);
}
This all works smoothly for the most part, but every now and then, the decrypted value comes back with a few weird characters in it.
For example: rsmi3�6CTΣ%mecompany.com was returned instead of rsmith#somecompany.com.
I'm not sure if it's the input or the output that is bad, but I'm guessing it has something to do with the uploaded CSV file... encoding issue? What do those characters mean and under what conditions are they produced?
UPDATE
Here's the code I'm using to add the encrypted value to the CSV:
$file = fopen(get_stylesheet_directory() . "/emma_members.csv", "r"); //Open the old file for reading
$newFile = fopen(get_stylesheet_directory() . "/emma_members_new.csv", "w"); //Create a new file for writing
if (!$file) error_log('ERROR opening file');
if (!$newFile) error_log('ERROR creating file');
$columns = ['email', 'member_id', 'member_since', 'plaintext_preferred', 'bounce_count', 'status_name', 'last_modified_at', 'city', 'first_name', 'last_name', 'request-demo', 'job-function', 'title', 'country', 'current-ams', 'opt-in', 'address-2', 'unique-identifier', 'state', 'postal_code', 'web-address', 'address', 'phone-number', 'company', 'area-of-specialization', 'work-phone'];
while (($data = fgetcsv($file)) !== FALSE) {
$row = array_combine($columns, $data);
$email = "{$row['email']}";
$uid = my_encrypt($email, ENCRYPT_KEY_1);
$row['unique-identifier'] = $uid;
$ret = fputcsv($newFile, array_values($row));
}
UPDATE 2
So after much testing with thousands of emails, it seems the my_encrypt function returns some bad values, depending on the input of course. It didn't happen with EVERY email address, but even 1 is too many for my use case.
I even tried getting rid of the :: between the data and the iv, but that didn't work either (although it's possible I did it wrong).
Anyway, I ended up using the following function in its place, and all is well:
function encrypt_decrypt($action, $string) {
$output = false;
$encrypt_method = "AES-256-CBC";
$secret_key = PHRASE_1;
$secret_iv = PHRASE_2;
// 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;
}
I tested your encrypt and decrypt functions and they are working as expected, so the reason for the behaviour seems to be a different file encoding on your device.
Especially when reading a CSV-file sometimes a (windows) device changes the encoding and you get some curious characters like those you have shown. My recommendation is to read the files with another encoding as the default one (ISO...).
I setup a live example that "proves" the correctness on a simple string en- and decryption: https://paiza.io/projects/e/Y-1gy9Y3b-VAlXAMG4odng
The result is simple:
plaintext: rsmith#somecompany.com
ciphertext: Y0RrMWRwR1pWeGtGbFdic3dIVmFzVmp4VUFYemJGdUhzMStRSll6akIwWT06Orf+twLGopVa4083RckEw44=
decryptedtext: rsmith#somecompany.com
Here is the code:
<?php
function my_encrypt($data, $key)
{
// Remove the base64 encoding from our key
$encryption_key = base64_decode($key);
// Generate an initialization vector
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
// Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
$encrypted = openssl_encrypt($data, 'aes-256-cbc', $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);
}
function my_decrypt($data, $key)
{
// Remove the base64 encoding from our key
$encryption_key = base64_decode($key);
// To decrypt, split the encrypted data from our IV - our unique separator used was "::"
list($encrypted_data, $iv) = explode('::', base64_decode($data), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $encryption_key, 0, $iv);
}
$plaintext = 'rsmith#somecompany.com';
echo 'plaintext: ' . $plaintext . PHP_EOL;
$encryptionKey = base64_encode(32);
$ciphertext = my_encrypt($plaintext, $encryptionKey);
echo 'ciphertext: ' . $ciphertext . PHP_EOL;
$decryptedtext = my_decrypt($ciphertext, $encryptionKey);
echo 'decryptedtext: ' . $decryptedtext . PHP_EOL;
?>
I am doing AES encryption on ios End, and i the base64 encode that string and send over to php end. On the php end, i have following code:
<?php
$key = 'a16byteslongkey!';
$data = base64_decode('LsCH4nvvGPKN67v94Ig9BweQgOk9rtDdK7ZugeJkTS8=');
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = substr($data, 0, $iv_size);
function aes256_cbc_decrypt($key, $data, $iv) {
if(32 !== strlen($key)) $key = hash('SHA256', $key, true);
if(16 !== strlen($iv)) $iv = hash('MD5', $iv, true);
$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_ECB, $iv);
$padding = ord($data[strlen($data) - 1]);
return substr($data, 0, -$padding);
}
$result = aes256_cbc_decrypt($key,$data,$iv);
var_dump($result);
?>
But when i run this code i get this "anil.mnd#gmail.cA���u�" . I should have got anil.mnd#gmail.com. I get only first 16 characters correct.
I am new to encryption so not have much idea what is wrong.
I am new to encryption so not have much idea what is wrong.
If you're new and want something that "just works", use defuse/php-encryption instead of trying to write it yourself.
If you're up for the challenge, however, keep reading:
Your code is unreadable. Let's add some whitespace.
$key = 'a16byteslongkey!';
$data = base64_decode('LsCH4nvvGPKN67v94Ig9BweQgOk9rtDdK7ZugeJkTS8=');
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = substr($data, 0, $iv_size);
function aes256_cbc_decrypt($key, $data, $iv)
{
if (32 !== strlen($key)) {
$key = hash('SHA256', $key, true);
}
if (16 !== strlen($iv)) {
$iv = hash('MD5', $iv, true);
}
$data = mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$key,
$data,
MCRYPT_MODE_ECB,
$iv
);
$padding = ord($data[strlen($data) - 1]);
return substr($data, 0, -$padding);
}
$result = aes256_cbc_decrypt($key,$data,$iv);
var_dump($result);
Specific problems:
You're using MCRYPT_MODE_ECB for a function named aes256_cbc (have you seen the penguin?)
When I switch that out, I get invalid data.
Your encryption method is also probably broken, since changing your IV to "\x00\x00"... makes it decrypt.
Specific recommendations:
Please, please, PLEASE consider using well-studied cryptography code instead of writing it yourself.
strlen() and substr() are brittle. See: function overloading.
Use a real key derivation function, not a hash function.
Your IV (and keys, for that matter) should be generated from a cryptographically secure pseudo-random number generator, such as random_bytes().
Use authenticated encryption.
I need to decode a 3des string in a php and I have no experience in decripting so far...
First step is: get the key and the set of strings to decode - I have that already.
I have this information about algorythm:
type: CBC,
padding - PKCS5,
initialization vector (iv?) - array of eight zeros
I try this way:
// very simple ASCII key and IV
$key = "passwordDR0wSS#P6660juht";
$iv = "password";
//$iv = array('0','0','0','0','0','0','0','0');
//$iv = "00000000";
$cipher = mcrypt_module_open(MCRYPT_3DES, '', 'cbc', '');
//$iv = mcrypt_enc_get_iv_size($cipher);
// DECRYPTING
echo "<b>String to decrypt:</b><br />51196a80db5c51b8523220383de600fd116a947e00500d6b9101ed820d29f198c705000791c07ecc1e090213c688a4c7a421eae9c534b5eff91794ee079b15ecb862a22581c246e15333179302a7664d4be2e2384dc49dace30eba36546793be<br /><br />";
echo "<b>Decrypted 3des string:</b><br /> ".SimpleTripleDesDecrypt('51196a80db5c51b8523220383de600fd116a947e00500d6b9101ed820d29f198c705000791c07ecc1e090213c688a4c7a421eae9c534b5eff91794ee079b15ecb862a22581c246e15333179302a7664d4be2e2384dc49dace30eba36546793be')."<br />";
function SimpleTripleDesDecrypt($buffer) {
global $key, $iv, $cipher;
mcrypt_generic_init($cipher, $key, $iv);
$result = rtrim(mdecrypt_generic($cipher, hex2bin($buffer)), "\0");
mcrypt_generic_deinit($cipher);
return $result;
}
function hex2bin($data)
{
$len = strlen($data);
return pack("H" . $len, $data);
}
At the beginnig you see example data, and on this data code works fine. Problem starts when I try to use my own data I get from database by SOAP webservice. I see this error:
Warning: pack() [function.pack]: Type H: illegal hex digit in....
I get this despite making attempts with different types of codings in the script. Script file itself is in ANCI.
Also: as you see in comments I also have made some experiments with IV but it doesn't make sense without dealing with first problem I gues.
Another thing is padding == PKCS5. Do I need to use it, and how should I do it in my case?
I would really appreciate help with this.
Ok, I have found a solution based mostly on this post: PHP Equivalent for Java Triple DES encryption/decryption - thanx guys and +1.
$iv = array('0','0','0','0','0','0','0','0');
echo #decryptText($temp->patient->firstName, $deszyfrator->return->rawDESKey, $iv);
echo #decryptText($temp->patient->surname, $deszyfrator->return->rawDESKey, $iv);
function decryptText($encryptText, $key, $iv) {
$cipherText = base64_decode($encryptText);
$res = mcrypt_decrypt("tripledes", $key, $cipherText, "cbc", $iv);
$resUnpadded = pkcs5_unpad($res);
return $resUnpadded;
}
function pkcs5_unpad($text)
{
$pad = ord($text{strlen($text)-1});
if ($pad > strlen($text)) return false;
if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
return substr($text, 0, -1 * $pad);
}
However I get a warning (hidden with # now). "Iv should have the same lenght as block size" - I tried all combinations I could figure out and I can't get rid of it. Any idea people?
Edit: secondary problem fixed to. This kode will fix the iv:
$iv_size=mcrypt_get_iv_size("tripledes","cbc");
$iv = str_repeat("\0", $iv_size); //iv size for 3des is 8
Ok, I have tried to create my own encryption/decryption methods using PHP mcrypt, and when I posted them a while back some called them "trash". They were mentioning things about "Initialization Vectors" and such. Basically, how can I make these cryptography methods better:
function encrypt($key, $data){
$encrypted_data = mcrypt_cbc(MCRYPT_RIJNDAEL_192, $key, $data, MCRYPT_ENCRYPT);
return base64_encode($encrypted_data);
}
function decrypt($key, $encryptedData){
$dec = base64_decode($encryptedData);
$decrypt = mcrypt_cbc(MCRYPT_RIJNDAEL_192, $key, $dec, MCRYPT_DECRYPT);
return trim($decrypt);
}
I want these to work the best they can except I am a duck in a brand new world when it comes to mcrypt, any suggestions are welcome, thanks!
Here is a snippet of the mcrypt functions I use. They use mcrypt_generic and mdecrypt_generic, which should be used according to the PHP manual.
function encrypt($key, $data){
$b = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$enc = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($enc), MCRYPT_DEV_URANDOM);
mcrypt_generic_init($enc, md5($key), $iv);
// PKCS7 Padding from: https://gist.github.com/1077723
$dataPad = $b-(strlen($data)%$b);
$data .= str_repeat(chr($dataPad), $dataPad);
$encrypted_data = mcrypt_generic($enc, $data);
mcrypt_generic_deinit($enc);
mcrypt_module_close($enc);
return array(
'data' => base64_encode($encrypted_data),
'iv' => base64_encode($iv)
);
}
function decrypt($key, $iv, $encryptedData){
$iv = base64_decode($iv);
$enc = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($enc, md5($key), $iv);
$encryptedData = base64_decode($encryptedData);
$data = mdecrypt_generic($enc, $encryptedData);
mcrypt_generic_deinit($enc);
mcrypt_module_close($enc);
// PKCS7 Padding from: https://gist.github.com/1077723
$dataPad = ord($data[strlen($data)-1]);
return substr($data, 0, -$dataPad);
}
I don't know much about mcrypt either, so I just kinda hacked these together. I md5 the key so it's always 32 characters (the max key length), and I randomly calculate an "Initialization Vector".
Using PKCS7 Padding is better because you can have strings that end in white space (as trim would remove that), also the encryption is more efficient when the string is a certain length.
I'm using AES 256 (MCRYPT_RIJNDAEL_256) here, but AES 192 (MCRYPT_RIJNDAEL_192) would work too.
Demo: http://ideone.com/WA5Tk
You can create an iv with mcrypt_create_iv(), using the appropriate size for your encryption mode.
$size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_192, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($size, MCRYPT_DEV_RANDOM);
Then pass it to mcrypt_cbc() as the optional 5th parameter. The only changes I've made here to your original functions are to pass in $iv:
function encrypt($key, $data, $iv){
$encrypted_data = mcrypt_cbc(MCRYPT_RIJNDAEL_192, $key, $data, MCRYPT_ENCRYPT, $iv);
return base64_encode($encrypted_data);
}
function decrypt($key, $encryptedData, $iv){
$dec = base64_decode($encryptedData);
$decrypt = mcrypt_cbc(MCRYPT_RIJNDAEL_192, $key, $dec, MCRYPT_DECRYPT, $iv);
return trim($decrypt);
}
I am coding a Drupal payment method module and within this I need to generate a hash to send to a bank. Bank asks me to code certain strings into the DES/ECB hash. They also provide test environment and here comes my problem. With the string B7DC02D5D6F2689E and key 7465737465703031 I should get result hash 3627C7356B25922B (after bin2hex, of course). This is by the bank test page and I have also checked this on this page: http://www.riscure.com/tech-corner/online-crypto-tools/des.html (encryption java applet).
My problem is that whatever I do I cant get my PHP code to provide the correct result. This is a simple function I am trying to use:
function encrypt($hash, $key)
{
$hash = strtoupper(substr(sha1($hash), 0, 16));
$key = strtoupper(bin2hex($key));
$block = mcrypt_get_block_size('des', 'ecb');
if (($pad = $block - (strlen($hash) % $block)) < $block) {
$hash .= str_repeat(chr($pad), $pad);
}
$sig = strtoupper(bin2hex(mcrypt_encrypt(MCRYPT_DES, $key, $hash, MCRYPT_MODE_ECB)));
return $sig;
}
and I have been trying sth like this as well:
function encrypt( $value, $key) {
$hash = strtoupper(substr(sha1($value), 0, 16));
$key = strtoupper(substr(bin2hex($key), 0, 16));
// encrypt hash with key
if (function_exists('mcrypt_module_open')) { // We have mcrypt 2.4.x
$td = mcrypt_module_open(MCRYPT_DES, "", MCRYPT_MODE_ECB, "");
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
mcrypt_generic_init($td, $key, $iv);
$signature = strtoupper(bin2hex(mcrypt_generic ($td, $hash)));
mcrypt_generic_end ($td);
}
else
{ // We have 2.2.x only
$signature = strtoupper(bin2hex(mcrypt_ecb (MCRYPT_3DES, $key, $hash, MCRYPT_ENCRYPT)));
}
return $signature;
}
None of these gave the correct signature. Any idea what's wrong? For now I am dealing with this issue more than 3 hrs, so I appreciate any help. I am not very familiar with this encryption stuff. Thanks a lot.
Btw.: Those $hash and $key mentioned above are after the strtoupper, substr and bin2hex functions at the beginning of my code snippets.
Simple solution:
function encrypt($hash, $key) {
return mcrypt_encrypt("des", pack("H*", $key), pack("H*", $hash), "ecb");
}
print bin2hex(encrypt("B7DC02D5D6F2689E", "7465737465703031"));
This prints 3627c7356b25922b for me, so it looks like that's working.
You were on the right track with bin2hex(), but that was converting in the wrong direction. (There's unfortunately no hex2bin() function, so you have to use pack() instead.)
You also don't need an IV for a single-block encryption like this.
You plaintext, B7DC02D5D6F2689E, is 8 bytes = 64 bits. This is an exact block for DES so you don't need any padding in ECB mode. I suggest that you remove the padding code entirely. All DEC-ECB needs in this case is the block to encrypt and the key; no padding and no IV.