Javascript encryption that match with PHP decryption [duplicate] - javascript

This question already has answers here:
Encrypt with CryptoJS and decrypt with PHP
(3 answers)
Closed 6 years ago.
I have the php code as below, and I want to create a Javascript functions that work the same as the php code below. Also, the data that encrypted in Javascript can also decrypt in php.
`
<?php
class Security {
public static function encrypt($input, $key) {
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$input = Security::pkcs5_pad($input, $size);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, $key, $iv);
$data = mcrypt_generic($td, $input);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$data = base64_encode($data);
return $data;
}
private static function pkcs5_pad ($text, $blocksize) {
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
public static function decrypt($sStr, $sKey) {
$decrypted= mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$sKey,
base64_decode($sStr),
MCRYPT_MODE_ECB
);
$dec_s = strlen($decrypted);
$padding = ord($decrypted[$dec_s-1]);
$decrypted = substr($decrypted, 0, -$padding);
return $decrypted;
}
}
?>
`

Use crypto.js at javascript end. Reference over here
Javascript
var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
/*
if you wish to have a more friendly key, you can convert letters to Hex this way:
var a = "D";
var hex_D = a.charCodeAt(0).toString(16);
just to mention,
if it were to binary, it would be:
var binary_D = a.charCodeAt(0).toString(2);
*/
var secret = "secret key goes here";
//crypted
var encrypted = CryptoJS.AES.encrypt(secret, key, {iv : iv});
//and the ciphertext put to base64
encrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
//Assuming you have control on the server side, and know the key and iv hexes(we do),
//the encrypted var is all you need to pass through ajax,
//Let's follow with welcomed pure JS style, to reinforce one and other concept if needed
var xh = new XMLHttpRequest();
xh.open("POST", "php.php", true);
xh.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xh.send("encrypted=" + encodeURIComponent(encrypted));
xh.onreadystatechange = function() {
if (xh.readyState == 4 && xh.status == 200) {
var myArr = (xh.responseText);
console.log(myArr);
}
};
At PHP
<?php
//Here we have the key and iv which we know, because we have just chosen them on the JS,
//the pack acts just like the parse Hex from JS
$key = pack("H*", "0123456789abcdef0123456789abcdef");
$iv = pack("H*", "abcdef9876543210abcdef9876543210");
//Now we receive the encrypted from the post, we should decode it from base64,
$encrypted = base64_decode($_POST["encrypted"]);
$shown = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
echo trim($shown);
//Although the decrypted is shown, there may be needed to trim and str_replace some \r \n \x06 \x05, if there is not a better "trim" way to do it though
?>
You can use JXG compressor too

Related

CryptoJS aes encrypt function PHP equivalent

I am trying to create a PHP equivalent to this JS code with CryptoJS:
function aesEncrypt (data) {
const key = 'GSTEGSTEjdfheyhdHSHSHSHDHHDHmdjjdn12ndndn5r=';
const iv = '\0';
const cipher = CryptoJS.AES.encrypt(data, CryptoJS.enc.Base64.parse(key), {
iv: CryptoJS.enc.Utf8.parse(iv), // parse the IV
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
})
return cipher.toString()
}
result of the js code : pHjpwiyKq7Rf4dFcBMbm1w==
here is the PHP code I wrote by reading other stackoverflow questions. But it did not return the same result.
$plaintext = "plainText";
$method = 'aes-256-cbc';
$key = base64_encode("GSTEGSTEjdfheyhdHSHSHSHDHHDHmdjjdn12ndndn5r=");
$iv = hex2bin('00000000000000000000000000000000');
$ciphertext = openssl_encrypt(
$plaintext,
$method,
$key,
OPENSSL_RAW_DATA,
$iv
);
$ciphertext = base64_encode($ciphertext);
echo $ciphertext;
result of the PHP code : +YJOMi2vISmEXIjUZls3MA==
In the PHP code, the key must be Base64 decoded and not Base64 encoded:
$key = base64_decode("GSTEGSTEjdfheyhdHSHSHSHDHHDHmdjjdn12ndndn5r=");
With this change the desired ciphertext is created.
Note that the ciphertext is Base64 encoded by default if 0 is passed instead of OPENSSL_RAW_DATA in the fourth parameter of the openssl_encrypt() call. The explicit Base64 encoding of the ciphertext is then not necessary.
Keep in mind that a static IV is insecure. Usually during encryption a random IV is generated, which is passed to the decrypting side along with the ciphertext (typically concatenated).

encrypt in PHP just like javascript CryptoJS

I have javascript that encrypts and gives the string.
But I have to do this through PHP.
The method I have tried is giving me different result than javascript.
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"
integrity="sha512-nOQuvD9nKirvxDdvQ9OMqe2dgapbPB7vYAMrzJihw5m+aNcf0dX53m6YxM4LgA9u8e9eg9QX+/+mPu8kCNpV2A==" crossorigin="anonymous"></script>
var txt="This text will be encrypted.";
var key = CryptoJS.enc.Hex.parse('0123456789abcdef0123456789abcdef');
var iv = CryptoJS.enc.Hex.parse('abcdef9876543210abcdef9876543210');
var encrypted = CryptoJS.AES.encrypt((txt), key, { iv: iv });
var encrypted_data = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
alert(encrypted_data);
I get output:
2X/btHgrMBhNlgD8oKNO9rzqCg+RSydprVKmpbYY+j0=
In PHP
<?php
$plaintext="This text will be encrypted.";
$key = pack("H*", "0123456789abcdef0123456789abcdef");
$iv = pack("H*", "abcdef9876543210abcdef9876543210");
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $iv);
$ciphertext_base64 = base64_encode($ciphertext);
echo $ciphertext_base64;
?>
I get output:
2X/btHgrMBhNlgD8oKNO9nJyd4xC4VTLGxnnrzGim+U=
I want the out as same as the output in javascript.
I found that the starting 21 characters match, but not the rest of the string.
Anything I am missing?
This is due to the different padding. CryptoJS uses PKCS#7, mcrypt applies zero padding.
You should replace mcrypt on the PHP side as it is deprecated (s. here). An alternative would be PHP/OpenSSL, which uses PKCS#7 by default.
A solution with PHP/OpenSSL which produces the same ciphertext is:
<?php
$plaintext="This text will be encrypted.";
$key = hex2bin("0123456789abcdef0123456789abcdef");
$iv = hex2bin("abcdef9876543210abcdef9876543210");
$ciphertext_base64 = openssl_encrypt($plaintext, "aes-128-cbc", $key, 0, $iv);
echo $ciphertext_base64; // 2X/btHgrMBhNlgD8oKNO9rzqCg+RSydprVKmpbYY+j0=
?>

How to encrypt JSON object with JS-NaCl and decrypt with php Libsodium

I managed to find a Libsodium js library (JS-NaCl) for front end encryption and has setup my PHP backend for Libsodium encrypt/decrypt also. When I encrypt a JSON object like below
const key = "827ccb0eea8a706c4c34a16891f84e7b";
const nonce = "0123456789abcdefghijvbnm";
var credentials = {
"zip":"265",
"account_number":"10028979739",
"passcode":"1234",
"account_type":"personal",
"request":"login",
"device":"iPhone 11"
};
function encrypt(data){
return sodium.crypto_secretbox(sodium.encode_utf8(data),nonce,key);
};
function decrypt(data){
return sodium.decode_utf8(sodium.crypto_secretbox_open(data, nonce, key));
}
function login(data){
$.ajax({
url:baseURL+"account/account.php",
method:"POST",
contentType:"application/x-www-form-urlencoded",
dataType:"json",
data:"datax="+JSON.stringify(encrypt(credentials)),
beforeSend:()=>{
console.log(credentials);
},success:(response)=>{
console.log(response);
},error:(e)=>{
swal("Connection Error","Failed to connect to the server!","error");
}
});
}
When I fire the login method with it encrypts the JSON object using the encrypt method hence I send something like this:
datax: {"0":191,"1":118,"2":248,"3":134,"4":45,"5":163,"6":3,"7":157,"8":78,"9":73,"10":157,"11":137,"12":178,"13":6,"14":68,"15":91,"16":217,"17":219,"18":50,"19":11,"20":127,"21":177,"22":130,"23":25,"24":209,"25":254,"26":210,"27":44,"28":119,"29":13,"30":144}
at the php backend code I am doing this:
<?php
function decrypt($data){
$key = "e9897cea109576c2f8088c277125d553e4f83afbc0abbb92cfb1f7b776b4fee0";
$nonce = "0123456789abcdefghijvbnm";
return sodium_crypto_secretbox_open($data,$nonce,$key);
}
function encrypt($data){
$data = utf8_encode($data);
$key = "e9897cea109576c2f8088c277125d553e4f83afbc0abbb92cfb1f7b776b4fee0";
$nonce = "0123456789abcdefghijvbnm";
return sodium_crypto_secretbox($data,$nonce,$key);
}
$credentials = $_POST["datax"];
echo decrypt($credentials);
?>
Same Key, Same nonce but it doesn't echo back anything. How to I decrypt this??
The code needs some changes. On the JavaScript side (frontend):
The JavaScript object must be converted into a string.
Besides the data, nonce and key must also be encoded using Utf8. Although the key could also be hexadecimal encoded to a 16 bytes key, in this context it must be Utf8 encoded to a 32 bytes key, because sodium.crypto_secretbox expects a 32 bytes key. The expected nonce must be 24 bytes in size.
Now the data can be encrypted.
sodium.crypto_secretbox returns the data as Uint8Array, which must therefore be encoded for transfer into a suitable format, e.g. hexadecimal.
The corresponding code is:
nacl_factory.instantiate(function (sodium) {
var credentials = {
"zip":"265",
"account_number":"10028979739",
"passcode":"1234",
"account_type":"personal",
"request":"login",
"device":"iPhone 11"
};
// Convert JavaScript object to string
var data = JSON.stringify(credentials);
// Utf8 encode key, nonce and data
var keyUtf8 = sodium.encode_utf8("827ccb0eea8a706c4c34a16891f84e7b");
var nonceUtf8 = sodium.encode_utf8("0123456789abcdefghijvbnm");
var dataUtf8 = sodium.encode_utf8(data);
// Encrypt
var encrypted = sodium.crypto_secretbox(dataUtf8, nonceUtf8, keyUtf8);
// Hex encode encrypted data for transfer
var encryptedHex = sodium.to_hex(encrypted);
console.log("Ciphertext (hex):\n" + encryptedHex.replace(/(.{64})/g, "$1\n"));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-nacl/1.3.2/nacl_factory.js"></script>
On the PHP side (backend):
The hexadecimal string $encryptedHex must be decoded.
The decoded data are to be decrypted. For this, key and nonce of the encryption must be used. In the posted code a different key is used, which is not possible in the context of crypto_secretbox (symmetric encryption), i.e. both sides use the same key. For asymmetric encryption there is crypto_box.
The result can be decoded into a JavaScript object whose objects can be accessed as usual.
The corresponding code is:
// Hex decode
$encrypted = sodium_hex2bin($encryptedHex);
// Decrypt
$nonce = "0123456789abcdefghijvbnm";
$key = "827ccb0eea8a706c4c34a16891f84e7b";
$decrypted = sodium_crypto_secretbox_open($encrypted, $nonce, $key);
// Convert to JavaScript object
$decryptedJSON = json_decode($decrypted);
echo "Zip: " . $decryptedJSON->zip . "\n";
echo "Account number: " . $decryptedJSON->account_number . "\n";
echo "Passcode: " . $decryptedJSON->passcode . "\n";
echo "Account type: " . $decryptedJSON->account_type . "\n";
echo "Request: " . $decryptedJSON->request . "\n";
echo "Device: " . $decryptedJSON->device . "\n";

PHP AES Encryption into NodeJS using crypto module

My task is to follow the given and working PHP encryption into node.js. using any node module packages. I dont need to do the decryption because its already existing in their API and i just need to pass the encypted value to their API for decryption which is in PHP. I tried using node-crypto and seperate md5 module. here is the pseudo code:
Data Encryption Algorithm
Create a 16-byte random salt and hash. The salt is newly created every time you call
the API.
Hash the encryption key provided.
Encrypt using 'AES-128-CBC' and use the hashed salt value as vector and hashed
encryption key.
Prefix salt and append the encrypted data.
Do Base64 encoding.
JSON encode and post request
I think I'm almost done just few steps to get a successful response here is my current node.js code
Node:
const reqBody = {
"username": "jCpVyf3VEt",
"password": "eGD6TWKmnn",
"account_no": "0030300155398",
"tran_date": "08/06/2019 10:30:45",
"reference_no": "12328ALHYGZC20",
"area": "JENRA DAU"
};
const Serialize = require('php-serialize')
const md5 = require('md5');
//encrypt
const crypto = require('crypto'),
algorithm = 'aes-128-cbc',
key = 'IfZDGbVDHTxlJIkK',
inputEncoding = 'utf8',
outputEncoding = 'base64';
function encrypt(data, key) {
let salt = crypto.randomBytes(16);
let hKey = md5(key);
let iv = md5(salt);
let serialized = Serialize.serialize(data);
let cipher = crypto.createCipheriv(algorithm, Buffer.from(hKey, 'hex'), Buffer.from(iv, 'hex'));
let crypted = cipher.update(serialized, inputEncoding, outputEncoding);
crypted += cipher.final(outputEncoding);
let encrypted = salt.toString('base64') + crypted.toString();
return encrypted;
}
encrypt(JSON.stringify(reqBody), key);
here is the working php code:
$data = json_encode([
'username' => "jCpVyf3VEt",
'password' => "eGD6TWKmnn",
'account_no' => "0030300155398",
'tran_date' => "08/06/2019 10:30:45",
'reference_no' => "12328ALHYGZC20",
'area' => "JENRA DAU"]);
function encrypt( $data) {
$key = md5("IfZDGbVDHTxlJIkK", true);
$cipher = "aes-128-cbc";
$salt = openssl_random_pseudo_bytes(16);
$iv = md5( $salt, true);
$encrypted_bin = $salt . openssl_encrypt( serialize( $data ), $cipher, $key, true, $iv);
$encrypted_str = base64_encode( $encrypted_bin);
return $encrypted_str;
}
echo encrypt($data);
for testing purpose here is the PHP code from their API for decryption:
$data = 'LI5BJJw1PEhWellnjKEt3g9oaHs8uDDknBT2qDNI7Rfs644+IjobOaFxlrIrOvDm7dkASRsOTu4Yuxzi4I5q29QoE5huH6y4/XZXsResZjLPidv1ToTnhB2UKXH5rX/g/8Od7ljO6VLVAS7zx+94xeOgtpP/idkkpDi1fRNGvnOkl1c6fcyVhwl2Pv+ijKSK9+ou+54dfQrCng2uBzKC6RrHY3lvP7ktsSvtnkXFqksrpjfJ2gnMH6sMIMzru1+D';
function decrypt($encrypted) {
$cipher = "aes-128-cbc";
$key = md5("IfZDGbVDHTxlJIkK", true);
$data = base64_decode($encrypted);
$salt = substr($data, 0, 16);
$iv = md5($salt, true);
$decrypted_bin = openssl_decrypt(substr($data, 16, strlen($data)), $cipher, $key, true, $iv);
if($decrypted_bin === false) {
return json_encode([ -102 => "Authentication Failed"]);
}
return unserialize( $decrypted_bin);
}
echo decrypt($data);
Running the PHP encryption code result a success response from the PHP decryption. But when I run my Node.js encryption I'm able to get an encrypted data but when I test the encrypted data from my Node.js and send the encrypted value into the PHP decryption code the result is authentication error. seems I'm not able to translate the PHP encryption algo into Node
.js
This is a very interesting one.. I think the main issue is the method of concatenating our salt and encrypted data in Node.js.
I found the following code worked nicely, it's giving us the same result as the PHP code.
Note that I'm using a fixed salt here (decoding from a fixed base64 string), since this gives us a deterministic output. You should probably switch to using crypto.randomBytes for this in production.
Also, PHP encodes "/" as "\/" in json output, see json_encode. This can be configured of course in PHP, (using the flag JSON_UNESCAPED_SLASHES in json_encode), but I suspect it can't be changed in your case. This is why I am replacing "/" with "\/" in the json plaintext.
const reqBody = {
"username": "jCpVyf3VEt",
"password": "eGD6TWKmnn",
"account_no": "0030300155398",
"tran_date": "08/06/2019 10:30:45",
"reference_no": "12328ALHYGZC20",
"area": "JENRA DAU"
};
const Serialize = require('php-serialize')
const md5 = require('md5');
//encrypt
const crypto = require('crypto'),
algorithm = 'aes-128-cbc',
key = 'IfZDGbVDHTxlJIkK',
inputEncoding = 'utf8',
outputEncoding = 'base64';
function encrypt(input, key, salt) {
let serialized = Serialize.serialize(input);
let iv = md5(salt);
let hKey = md5(key);
let cipher = crypto.createCipheriv(algorithm, Buffer.from(hKey, 'hex'), Buffer.from(iv, 'hex'));
let crypted = cipher.update(serialized, inputEncoding);
let encrypted = Buffer.concat([salt, crypted, cipher.final()]);
return encrypted.toString(outputEncoding);
}
// We must escape forward slashes here, since this is how PHP will behave.
let data = JSON.stringify(reqBody).replace(/\//ig, "\\/");
// We can use any random salt here, e.g. crypto.randomBytes For this example we'll use the same as the existing encrypted data.
let salt = Buffer.from('LI5BJJw1PEhWellnjKEt3g==', 'base64');
console.log("Encrypted data: ", encrypt(data, key, salt));

AES Encryption with CryptoJS and PHP

I want to implement AES encryption using JavaScript. Used AES CBC Mode. I have managed to do it in PHP. It looks like:
public function encrypt($value) {
if (empty($value)) {
return $value;
}
$value = Unicode::convertToUtf8($value, 'UTF-8');
if ($key = $this->getEncryptionKey()) {
// Generates from key 1st 16 bytes.
$iv = mb_substr($key, 0, 16);
//encrypt message with key
$message = openssl_encrypt($value, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
return base64_encode($message);
}
}
public function getEncryptionKey() {
$key = 'secret';
$key = Unicode::convertToUtf8($key, 'UTF-8');
// Make sure the key is the correct size.
if (strlen($key) < 32) {
$key = str_pad($key, 32, "\0");
}
if (strlen($key) > 32) {
$key = mb_substr($key, 0, 32);
}
return $key;
}
If I give $value = retest2; it gives me ukUCH0SvgdmM8vTqQumAVg== output
I know it's right, I tried it using C# as well got the same result. But when I try to replicate this using JavaScript, I wasn't able to produce same PHP's output. Below is the javascript code that I have tried:
const message = utf8.encode('retest2');
const password = utf8.encode('secret').padEnd(32, '\0');
const key = CryptoJS.enc.Hex.parse(password);
const iv = CryptoJS.enc.Hex.parse(password.substring(0, 16));
const encrypted = CryptoJS.AES.encrypt(message, key, {
iv: iv
});
console.log(btoa(encrypted.toString()));
Using the same value I get dzd4bjNwenduQT09.
I have also read other similar questions asked on the same topic here, but I can't seem to figure out where I am going wrong?
Thanks!
As #symcbean said,
You shouldn't be using the key or data derived from it as your
initialization vector.
I assume, you have no option and you have to use key or data derived from it as your initialization vector.
A few months ago, I had the exact same situation and I did something like this,
const message = 'retest2';
let password = 'secret';
if (password.length < 32) {
password = password.padEnd(32, '\0');
}
const iv = CryptoJS.enc.Utf8.parse(password.substring(0, 16));
password = CryptoJS.enc.Utf8.parse(password);
const encrypted = CryptoJS.AES.encrypt((message), (password), {
iv: iv
});
console.log(CryptoJS.enc.Base64.stringify(encrypted.ciphertext));
These should be comments but space is limited....
You shouldn't be using the key or data derived from it as your initialization vector.
I know it's right, I tried it using C# as well
You should have shown us the code.
Your openssl call invokes the key derivation function to create a (better?) encryption key from the data you pass in the third parameter. OTOH, in crypto.js there is no implicit key derivation.

Categories