Blowfish decode from a string - php

Using the blowfish cbc mode, I want to create an encrypted token. When I create the token and immediately decrypt it, it works correctly. However, if I place the encrypted token in SESSION and try to decrypt it during a subsequent request, the decryption produces garbage.
This is my code. I am using SESSION for testing; eventually, I'll be storing these strings in a database instead.
session_start();
define("key","v8nga4r76qlipm111jnioool");
define("iv",substr(md5(uniqid(rand(),1)),0,8));
require_once("Crypt/Blowfish.php");
$str = "Blowfish_test";
// encode start!!
$blowfish = Crypt_Blowfish::factory("cbc", key, iv);
$encrypt = $blowfish->encrypt($str);
$encrypt64 = base64_encode($encrypt);
$_SESSION["test"] = $encrypt64;
So far, everything is correct. If, on the same request, I immediately decode it (from SESSION), the results are as expected:
// decode start!!
$blowfish = Crypt_Blowfish::factory("cbc", key, iv);
$decrypt64 = base64_decode($_SESSION["test"]);
$decrypt = $blowfish->decrypt($decrypt64);
$trim_decrypt = rtrim($decrypt, "\0");
echo "Before encryption : " . $str ."<br>";
echo "Encrypted string &nbsp&nbsp: " .$encrypt64. "<br>";
echo "decrypted string &nbsp&nbsp: " .$trim_decrypt. "<br>";
If I place only the second block of code on a different page (so a previously generated string is being decrypted), the decrypted string is garbage.
Encryption -> session["test"] -> Decryption --- no problem
session["test"] -> Decryption --- problem
What's happening here?

I'm sorry for this super delay.
I solved this problem by the comment at that time, so I close this question using community wiki.
--- add ---
problem is solved
// encode
// At the same time save the initialization vector
$_SESSION["test"] = $encrypt64;
$_SESSION["iv"] = iv;
--- other file ---
// decode
$blowfish = Crypt_Blowfish::factory("cbc", key, $_SESSION["iv"]);

Related

AES Encryption producing different results between PHP and Javascript

Hi there StackOverflow community,
After researching for countless of hours, I'm unable to find an explanation as to why my ouputs differ between javascript and my laravel application.
I could use input type hidden to make a post from my web browser, but that would defeat the purpose of having a secure client side processing and I fear that if I don't find the reason as to why this is happening, then decryption (which I plan to do through php) would not work either.
my php code is as follows:
$payload = "this is my plaintext";
$binary_signature = "";
$private_key = openssl_pkey_get_private(file_get_contents(storage_path('privatekey.pem')), 'enc123456789');
openssl_sign($payload, $binary_signature, $private_key, OPENSSL_ALGO_SHA256);
$signature = base64_encode($binary_signature);
$new_payload = $payload."&sign=".$signature; // where my actual plaintext is also used in my javascript code
$key = "thisismykey";
$iv = "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0";
$encryption = openssl_encrypt($new_payload, 'AES-256-CBC', $key,OPENSSL_RAW_DATA, $iv);
dd(base64_encode($encyrption));
which outputs the following:
AEyHK+4DQWYjw8GeVV3mfzUJtk7ylxZINeryAdFptEbDyKOVbmNg8z32J2JgxGpFsQKpXxWaqDLf0IPNIq1jof0rKWhhDpaWzvTd0Tq/zgze7oGtZzEIqtdRDqax3ZvPkzNfuO/O14iW/YTwFkm9FLy9kGIirZDUTuAcOIjXGCgxhrhZHLn+V6SZpW5dYnH8u5rPDCeez2/HkUPI71YjD6hZ0DRjIkiXCyjPYH4fjNykz4yXo8hD+489Zxm8QPq1O1dyjR9JXSdrlYMWdixt6w0vz8EtPC8gZ+bDP/N/UEK07M52VB693zYb5uD1u7WuUUtsOjkr5ocF6QbEW7sjzI4q9yAxqvxRW/bkKqodcVqtglW6YsdJjrTR0EfA/Or/QF3e3QWVM5/2g4rT3ccE17OP6Rp/46yTpW9lOgS6Qiz2hY95GoaxbLfHB/Vb0Es+UppwDu8bd/u2Qax5erBi5ObZu3AjKNpTem45paspsKH3/vc2Jc810XrVQPjnDdZ8VrXvCgPiulywn5Mj28O7uUQ5bay3Zxy3bmHb7ESDEVMKiSEoru3LzDJ7wwPlidJzPcfWtuiMEMsPOv1Y6LaxtlizWM5/zYJFX/RA4d+KBl+Rn6BoPZDcX/6eh3oUoNhy
My JS Code (the plaintext is received through an ajax call which has the same sign method as you see from the php code, the encryption plaintext is of the same value from my php code)
function encrypt(plaintext, secretkey) {
var randomSeed = "";
randomSeed = secretkey;
var key = CryptoJS.enc.Utf8.parse(randomSeed.toUpperCase());
var iv = CryptoJS.enc.Utf8.parse("0000000000000000");
var encrypt = {};
encrypt = CryptoJS.AES.encrypt(plaintext, key, {
mode : CryptoJS.mode.CBC,
iv : iv
});
console.log("encryption is "+encrypt.toString());
return encrypt.toString(encrypt.toString());
}
Returns the correct value which is:
jLmAUr+JyCjbpctU4z6+dlF61jbHRphwTS0iAk3IRiy3jkfCtaCSWdiIO0awuX6G1jAlZroTiAuMl9OW0zj0q4HitndfGFtFUoMMCqZTzvMr6cy1TyG9EFz20T6ByrBnOvGuoVjv3Flufuk2Ghz5in2W2A3T+wF+SPXX/bIAnHtE3uW0bPl2q5tn6KyUI1uoQaYcMZKRPyzAQS7WSSwSOmAcVrRuDANgZQuO+3mh86QAdeFaYqFdZUnxD4c2kkbkGy17SUFfSK8Qjv+8tkTcYXV9QRRdWjGZiQQeyAr3PDKA4SDVzrcMNwJjTaLJiZv0Iau66HGpbf2yvRDLtIOoXQmnhs6NKTZpcSwZ07hHqVvBZmNRq+jqZOGw1s8GRH+Bz4yxSRycTS0DEddhyMoxhZcUc5wt42vDOYIEH2nuw/uu4gjrwpx0rVO1ssoZYRxvBaA6zSC4N04Wdn4JE2/LtXertDLEdLBtmk3c3n4QDU0tK5v31HMY7P7+fdQXU62niVxCNPSt9dpYa82IUrQuigNXgrbphQvZNmmcONi/4pnxJjKcKYpCn/1KhkBVUAhYm6UKJJvMNAo0M+cfsvReImrJx6IzPRdzTTFAQF5kW2NFkV4EIb0DtCF679RtdAhg5ShaP7QhqYL6EgFCs9WnJTACC26TmV20DAqUiuIYULLtjDW4qFOWi/y8D1JOWTar
I don't understand why my PHP encryption is giving the wrong output while my JavaScript encryption is giving the correct output
I'm hoping someone could give me an insight as to what I'm doing wrong from my PHP side. Wha I'm hoping to achieve is that my PHP encryption will output the same result as my JavaScript encryption.
Thank you in advance :)
I have solved my issue.
To those having a similar issue, here's a brief explanation. Crypto JS uses the following:
var key = CryptoJS.enc.Utf8.parse(randomSeed.toUpperCase());
var iv = CryptoJS.enc.Utf8.parse("0000000000000000");
Which translates to a word array if you console log the output. PHP needs to have the similar word array value that cryptoJS produces for it's AES encryption method. To solve this, you have to convert your $key and iv to be in hex format and format your code in php such as:
$key = pack("H*", "4a424d56595753555047553830334d42505a314f414256414c5a565239324659");
$iv = pack("H*", "30303030303030303030303030303030");
Then when you proceed to using openssl_encrypt
$encrypted_data = openssl_encrypt($plaintext, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
you will get the same output as crpytoJS.
Hope this helps.

Generate a hashed message with SHA256 hmac in php (key & msg are fake)

I'm using php 8.0.11, i have to generate a SHA256 encrypted messagesignature.When i test the API in postman with javascipt code in Pre-request script it give the right encrypted messagesignature, i converted the script to php when i test it in php it sends a different wrong encrypted messagesignature (key & msg are fake) :
javascript code (Pre-request script in postman):
let msg='mymessage'
const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256,"myapipkey");
hmac.update(msg);
const messageSignature = hmac.finalize().toString();
pm.globals.set("messageSignature",messageSignature);
console.log('messageSi:',pm.globals.get('messageSignature'))
````
php code:
````php
$data_to_hash = "mymessage";
$data_hmac=hash('sha256', $data_to_hash);
$ctx = hash_init('sha256', HASH_HMAC, 'myapipkey');
hash_update($ctx, $data_hmac);
$result = hash_final($ctx);
echo $result;
````
A simple change to the PHP code should give the correct result.
It looks like you were hashing twice (or something like that!)
$data_to_hash = "mymessage";
$ctx = hash_init('sha256', HASH_HMAC, 'myapipkey');
hash_update($ctx, $data_to_hash);
$result = hash_final($ctx);
echo $result;
In any case, the output of the above code will be:
898786a1fa80da9b463c1c7c9045377451c40cf3684cbba73bdfee48cd3a5b8f
Which is the same as the JavaScript code, both match the output given here:
https://codebeautify.org/hmac-generator
With Algorithm = 'SHA256', Key = 'myapipkey' and Plaintext = 'mymessage'.

Convert PHP openssl_encrypt with md5 to NodeJS using Crypto

I have this code in PHP (can't modify)
<?php
$myPlain = "123456789012345678900000";
$myKey = md5($myPlain, true);
$myKey .= substr($myKey, 0,8);
$encrypted = openssl_encrypt($myPlain, 'des-ede3', $myKey, OPENSSL_RAW_DATA);
print(base64_encode($encrypted));
This code returns
FTYDrZTZMjVBv5Fk/xcfFxJASaizzrmoPts7fGDvWjc=
When I try to replicate this in NodeJS
function testEde3(myKey, myPlain) {
try {
let md5Key = crypto.createHash('md5').update(myKey, 'utf-8').digest("base64").substr(0, 24);
console.log(md5Key); //outputs 4o1aJrSWN3bSfjuIX6VXgA==
console.log(md5Key.length); //outputs 24
const cipher = crypto.createCipheriv('des-ede3', md5Key, null);
let encrypted = cipher.update(myPlain, 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
} catch ( ex ) {
return ex;
}
}
const myKey = "123456789012345678900000";
const myPlain = "123456789012345678900000";
const hash = testEd3(myKey, myPlain);
console.log(`Hash is: ${hash}`);
The output is
Hash is: lDQX9OGsopKOt6P9WQwekGsKDQGFpfGW50zbs3HrOfQ=
I'm thinking the problem is on MD5. If I try to encrypt without md5, the results are the same.
Thanks
The key in Php code consists of the 16 bytes of the MD5 hash, to which the first 8 bytes of the MD5 hash are appended, resulting in a 24 bytes key (as required for 3DES). This is currently not implemented in the NodeJS code, but can be achieved e.g. by:
let md5Key = crypto.createHash('md5').update(myKey, 'utf-8').digest();
md5Key = Buffer.concat([md5Key, md5Key.slice(0, 8)]);
With this change, the NodeJS code generates the ciphertext of the PHP code with the same input data.
des-ede3 means 3DES in ECB mode. Note that 3DES is outdated and slow and should be replaced by AES. ECB mode is generally insecure. Encrypting the key also makes little sense (but may only be for testing purposes).

Decrypt a signature using openssl_public_decrypt

I'm trying to verify an external call to one of our endpoints, this endpoint is triggered by a third party, we receive a transaction data and a signature based on that transaction information, with that, we need to decrypt the signature and compare the result to verify the authenticity.
I'm trying to use openssl_public_decrypt to decrypt the signature using the provider's public key.
This is how I'm trying:
$signature = 'GcTtinhU0YgwGbZPtBwLdh+zdEe0w0W95TFPggeHMCjeDUBWgZfCZ6ZDRUk7DfT5BkKsbAi8/4o60Krcwz1JMdRjmsPf7vj33heVIB2PZJaf8eFR1jijLIsyl4vgH7BbbQ2I6kk6IcYXYWPVAHYRWxl1pJwOyNxZPr49fdW+hcw2zbpkEmj2114QBSiV6eHLowVYKLvpuiT8zLc6DN/wVzCYBuR/cg+CPHgYMeWFsuvu9J46hm6Hij00E68ldYAqVwImlmHPqfqvdEItg3Oi0ac4tXH2nCNgLPHcyU/H32NzTYC9iT1YZkoInqsU6Qv64vbU9lSMS91EQBEa5UQkUg==';
$pubKey = openssl_pkey_get_public('file://path/to/public.pem');
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey)){
echo $data;
}else{
echo 'Error';
}
I don't get any error but the $data value is not what I expect, is something like this
v_~�#&�W��q�&Ș�uQ���֔�
I'm sure I'm missing something but I can't find out what is it, due to the $data value looks like is encrypted.
The result that I expect from the decrypt is 167619085f7ed94026e357930b18dc011971f226c898ef7551cdf6ec9ad694cf this is the result of the following code
$canonical = 'c328e942-8be8-4104-abbe-048254f893dc|9687|2874.30|52409|BP1381|550bd8439cd1f41691671cdd4e8c6ae6';
$hashed = hash('sha256', $canonical);
That last part is how the provider generates the signature.
For the given example, canonic form is as follows:
cec4b9bf-5a39-4bd7-bc8b826ebc18208d|Internal_0005|12|39679|BP7610|947d589a40dece13c28f2b63c41ae451
We sign the response by hashing the canonic form with SHA-256 and encrypting the
resulting bytes with our private key.
RSA_ENCRYPT(SHA256(canonicForm), privkey.key)
To verify the payload, you must recalculate the canonic form and apply SHA-256 to the
result. The resulting value must be compared with the result of decrypting the signature
parameter with our public key.
Any hint would be appreciated.
perhaps post the public key and some valid test data so we can test ourselves?
anyway, v_~�#&�W��q�&Ș�uQ���֔� could be a valid signature, remember that SHA256 is 256 random bits, it's binary data, not ascii data, not hex, and not printable. SHA256 is also exactly 32 bytes long (256 bits, and 1 byte is 8 bits, and 256/8 is 32 bytes), so if you run var_dump(strlen($data)) after decryption, it should print 32, if it does not print 32, it implies they're using a padding scheme, try checking the strlen of both OPENSSL_PKCS1_PADDING and OPENSSL_NO_PADDING , when you get the correct padding scheme, strlen($data) after decryption should be int(32)
but my best guess is:
$signature = 'GcTtinhU0YgwGbZPtBwLdh+zdEe0w0W95TFPggeHMCjeDUBWgZfCZ6ZDRUk7DfT5BkKsbAi8/4o60Krcwz1JMdRjmsPf7vj33heVIB2PZJaf8eFR1jijLIsyl4vgH7BbbQ2I6kk6IcYXYWPVAHYRWxl1pJwOyNxZPr49fdW+hcw2zbpkEmj2114QBSiV6eHLowVYKLvpuiT8zLc6DN/wVzCYBuR/cg+CPHgYMeWFsuvu9J46hm6Hij00E68ldYAqVwImlmHPqfqvdEItg3Oi0ac4tXH2nCNgLPHcyU/H32NzTYC9iT1YZkoInqsU6Qv64vbU9lSMS91EQBEa5UQkUg==';
$canonical = 'c328e942-8be8-4104-abbe-048254f893dc|9687|2874.30|52409|BP1381|550bd8439cd1f41691671cdd4e8c6ae6';
$pubKey = openssl_pkey_get_public('file://path/to/public.pem');
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey)){
echo "signature decryption success! ";
if(hash_equals(hash("sha256",$canonical,true),$data)){
echo "checksum verification success!";
} else{
echo "checksum verification failed (after decryption was successful..)";
}
}else{
echo 'checksum decryption error';
}
but again, experiment with both
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey, OPENSSL_PKCS1_PADDING)){
and
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey, OPENSSL_NO_PADDING)){
1 of them is probably correct (and when it is correct, var_dump(strlen($data)) should print int(32) )

mcrypt_ecb not $decrypted correctly when I use file_get_contents PHP

I have code which first I encrypted using mcrypt_ecb and hten then i send the value to another page using file_get_contents. but when I echo receive data, it only print half of it. I have attached my code and result below:
Client
$mac="B8-AC-6F-2D-5C-23";
// encrpt the max address
$key_value = pack('H*', "bcb04b7e103a0c");
$plain_text = $mac;
$encrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $plain_text, MCRYPT_ENCRYPT);
echo ("<p><b> Text after encryption : </b>");
echo ( $encrypted_text );
// send encrypted mac address to bridge for verification
$response = file_get_contents('http://localhost/scale/check.php?mac='.$encrypted_text);
print_r( $response );
Host
if (isset($_GET['mac']) && $_GET['mac']) {
$mac = $_GET['mac'];
echo "<br/>";
echo $mac;
//decrypted recieve data
$key_value = pack('H*', "bcb04b7e103a0c");
/* #var $encrypted_text type */
$encrypted_text = $mac;
$decrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $encrypted_text, MCRYPT_DECRYPT);
echo ("<p><b> Text after decryption : </b>");
echo ( $decrypted_text );
}
Result:
B8-AC-6F-2D-5C-23
Text after encryption : 5"ÆfÛkã–]» º"÷5Ù(Ÿ©U
_5"ÆfÛk_ã–]»
Text after decryption : 9Bþ‚î10tçæÇ|¤
The output of mcrypt_ecb is binary data, its bytes can contain arbitrary values. If you want to transfer it as an url parameter you have to encode it first. The general problem is that the url parameter parsing assumes a certain structure of the url. For example if you write http://localhost/scale/check.php?mac=abc123&foo=bar then PHP will see two parameters, the first one being mac with value abc123, the second being foo with value bar. But in the way you just concatenate the ciphertext to the url you could end up with the same url even if you really meant to have a parameter mac with the value abc123&foo=bar. To prevent such confusions all values have to be encoded so that certain characters do not appear. In your example the space seems to be problematic.
You can find some background on url encoding here: https://en.wikipedia.org/wiki/Percent-encoding .
By the way: It is generally appreciated if you only post a minimal example of your problem. Given that you have issues with the transmission of your data you could remove all the encryption code. But as I've seen it now, I can't help but notice that the algorithm DES and the block cipher mode ECB is generally a very insecure choice. If you want to protect your data during transmission you can avoid all the usual issues with home grown crypto schemes by using an https connection (notice the "s" for "secure", which forces your http client to connect to the server using TLS).

Categories