Encrypt in PHP openssl and decrypt in javascript CryptoJS - javascript

I'm encrypting some parameters in PHP using
openssl("parameter", "AES-256-ECB", "client")
and I wish to decrypt in CryptoJS:
CryptoJS.AES.decrypt(parameter, "client", {mode: CryptoJS.mode.ECB}).toString(CryptoJS.enc.Utf8);
but it's throwing an empty string.
Any suggestions?

CryptoJS: PHP openssl encrypt -> javascript decrypt
PHP:
function CryptoJSAesEncrypt($passphrase, $plain_text){
$salt = openssl_random_pseudo_bytes(256);
$iv = openssl_random_pseudo_bytes(16);
//on PHP7 can use random_bytes() istead openssl_random_pseudo_bytes()
//or PHP5x see : https://github.com/paragonie/random_compat
$iterations = 999;
$key = hash_pbkdf2("sha512", $passphrase, $salt, $iterations, 64);
$encrypted_data = openssl_encrypt($plain_text, 'aes-256-cbc', hex2bin($key), OPENSSL_RAW_DATA, $iv);
$data = array("ciphertext" => base64_encode($encrypted_data), "iv" => bin2hex($iv), "salt" => bin2hex($salt));
return json_encode($data);
}
$string_json_fromPHP = CryptoJSAesEncrypt("your passphrase", "your plain text");
JS:
function CryptoJSAesDecrypt(passphrase,encrypted_json_string){
var obj_json = JSON.parse(encrypted_json_string);
var encrypted = obj_json.ciphertext;
var salt = CryptoJS.enc.Hex.parse(obj_json.salt);
var iv = CryptoJS.enc.Hex.parse(obj_json.iv);
var key = CryptoJS.PBKDF2(passphrase, salt, { hasher: CryptoJS.algo.SHA512, keySize: 64/8, iterations: 999});
var decrypted = CryptoJS.AES.decrypt(encrypted, key, { iv: iv});
return decrypted.toString(CryptoJS.enc.Utf8);
}
console.log(CryptoJSAesDecrypt('your passphrase','<?php echo $string_json_fromPHP?>'));
CryptoJS: javascript encrypt -> PHP openssl decrypt
JS:
function CryptoJSAesEncrypt(passphrase, plain_text){
var salt = CryptoJS.lib.WordArray.random(256);
var iv = CryptoJS.lib.WordArray.random(16);
//for more random entropy can use : https://github.com/wwwtyro/cryptico/blob/master/random.js instead CryptoJS random() or another js PRNG
var key = CryptoJS.PBKDF2(passphrase, salt, { hasher: CryptoJS.algo.SHA512, keySize: 64/8, iterations: 999 });
var encrypted = CryptoJS.AES.encrypt(plain_text, key, {iv: iv});
var data = {
ciphertext : CryptoJS.enc.Base64.stringify(encrypted.ciphertext),
salt : CryptoJS.enc.Hex.stringify(salt),
iv : CryptoJS.enc.Hex.stringify(iv)
}
return JSON.stringify(data);
}
PHP:
function CryptoJSAesDecrypt($passphrase, $jsonString){
$jsondata = json_decode($jsonString, true);
try {
$salt = hex2bin($jsondata["salt"]);
$iv = hex2bin($jsondata["iv"]);
} catch(Exception $e) { return null; }
$ciphertext = base64_decode($jsondata["ciphertext"]);
$iterations = 999; //same as js encrypting
$key = hash_pbkdf2("sha512", $passphrase, $salt, $iterations, 64);
$decrypted= openssl_decrypt($ciphertext , 'aes-256-cbc', hex2bin($key), OPENSSL_RAW_DATA, $iv);
return $decrypted;
}
in mi tests I have used : github.com/sytelus/CryptoJS

PHP Encryption
function encryptPhp($string) {
$encrypt_method="AES-256-CBC";
$secret_key='secret_key';
$secret_iv='secret_iv';
$key=hash('sha256',$secret_key);
$iv=substr(hash('sha256',$secret_iv),0,16);
$output=openssl_encrypt($string,$encrypt_method,$key,0,$iv);
$output=base64_encode($output);
return $output
}
javascript Equialent
function decryptString($string) {
var Utf8 = CryptoJS.enc.Utf8;
const $secret_key='secret_key';
const $secret_iv='secret_iv';
const key= CryptoJS.SHA256($secret_key).toString(CryptoJS.enc.Hex).substring(0,32);
let iv= CryptoJS.SHA256($secret_iv).toString(CryptoJS.enc.Hex).substring(0,16);
const encrypt = CryptoJS.enc.Base64.parse($string).toString(CryptoJS.enc.Utf8);
const decrypt = CryptoJS.AES.decrypt(encrypt, Utf8.parse(key), { iv: Utf8.parse(iv)}).toString(Utf8);
return decrypt;
}
Also Don't use secret key & secret iv in browser side, it may affect security

Please use this method It's work for me.. Thanks
Typescript Code (Anuglar 4+) :
encryptUsingAES256() {
let _key = CryptoJS.enc.Utf8.parse(your_token_here);
let _iv = CryptoJS.enc.Utf8.parse(your_token_here);
let encrypted = CryptoJS.AES.encrypt(
this.request, _key, {
keySize: 16,
iv: _iv,
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
this.encrypted = encrypted.toString();
console.log(this.encrypted)
}
decryptUsingAES256() {
let _key = CryptoJS.enc.Utf8.parse(your_token_here);
let _iv = CryptoJS.enc.Utf8.parse(your_token_here);
this.decrypted = CryptoJS.AES.decrypt(
this.encrypted, _key, {
keySize: 16,
iv: _iv,
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).toString(CryptoJS.enc.Utf8);
console.log(this.decrypted)
}

Related

What is the equivalent of this function in Javascript/NodeJs?

This is the function used to encrypt in php
function generatetoken()
{
$token = ['id' => '123456','name' => 'username','email' => 'useremail#example.com','type' => 'user'];
$cipher = "AES-128-CBC";
$plaintext = json_encode($token);
$ivlen = openssl_cipher_iv_length($cipher = "AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, '123456789', $options = OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, '123456789', $as_binary = true);
$ciphertext = base64_encode($iv . $hmac . $ciphertext_raw);
return ciphertext;
}
I have this function to decrypt text in php :
function decodetokeninPhp($request_token)
{
$cipher = "AES-128-CBC";
$c = base64_decode($request_token);
$ivlen = openssl_cipher_iv_length($cipher = "AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len = 32);
$ciphertext_raw = substr($c, $ivlen + $sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, '123456789', $options = OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, '123456789', $as_binary = true);
if (hash_equals($hmac, $calcmac)) {
return json_encode($original_plaintext);
} else {
return null;
}
}
I want the equivalent of this in javascript/Nodejs, i tried this:
function decode(token){
var password = 'My-key';
const ivLength = 16;
const sha2len = 32;
let replacedToken = token.toString();
const base64decoded = Buffer.from(replacedToken, 'base64').toString('binary');
const iv = replacedToken.substr(0,ivLength);
const hMac= replacedToken.substr( ivLength,sha2len);
const ciphertext_raw = replacedToken.substr(ivLength+sha2len);
var DataEncrypt = ciphertext_raw;
var DataKey = CryptoJS.enc.Utf8.parse(password);
var DataVector = CryptoJS.enc.Utf8.parse(iv);
var decrypted = CryptoJS.AES.decrypt(DataEncrypt, DataKey, { iv: DataVector });
var decrypted = CryptoJS.enc.Utf8.stringify(JSON.stringify(decrypted));
console.log("token decoded"+ decrypted);
}
But the console.log just print "token decoded:", it's empty result :(
Please someone help me i'm going crazy :/
If you want to use the crypto module of NodeJS, the whole CryptoJS part has to be changed. A possible implementation with crypto is:
var crypto = require('crypto');
function decode(token){
var keyDec = Buffer.from('0123456789012345', 'utf8'); // sample key for encryption/decryption
var keyAuth = Buffer.from('0123456789', 'utf8'); // sample key for authentication
var ivLen = 16;
var macLen = 32;
var tokenBuf = Buffer.from(token, 'base64');
var iv = tokenBuf.slice(0, ivLen);
var mac = tokenBuf.slice(ivLen, ivLen + macLen);
var ciphertext = tokenBuf.slice(ivLen + macLen);
// Authenticate
var hmac = crypto.createHmac('sha256', keyAuth);
hmac.update(ciphertext);
var macCalc = hmac.digest();
if (macCalc.equals(mac)) {
// Decrypt, if authentication is successfull
var decipher = crypto.createDecipheriv("AES-128-CBC", keyDec, iv);
var decrypted = decipher.update(ciphertext, '', 'utf-8');
decrypted += decipher.final('utf-8');
return JSON.parse(decrypted);
} else {
console.log("Decryption failed");
}
}
var token = decode('U3pukkS48yeNpsusv43Tmv2AmmDfYVtQ8jPw2izEQ0CVOfutGtA9e3ZWXJo2Ibi2axo31blnW6uq/yCz/KRSltwGhCmwpiHQ8mP5ulMf0Nr9V9Gzr6r+R6y3ZOpzTsV9IEkaKDxZTihfoDAzeyN9LYKS9uUW6URL0Do1HGaZ51o='); // from PHP code
console.log(token);
Here, the ciphertext was generated with the posted PHP code using the sample keys 0123456789012345 and 0123456789 for encryption and authentication, respectively.
I am suspicious of the json_encode() in the PHP code for decryption. Here I would expect a json_decode() and thus, in the NodeJS code a JSON.parse() (but you can modify this as needed).

how to decrypt in nodejs which is encrypted using JAVA

im trying to convert java lambda into javascript lamda. want to convert these encrypt and decrypt method which is written in java to node js or javascript.
I have tried to implement using crpto in node
keys are like this
private static String CIPHER_NAME = "AES/CBC/PKCS5PADDING";
private static int CIPHER_KEY_LEN = 16; //128 bits
encrypt method
private String encrypt(String key, String iv, String data) {
try {
if (key.length() <CIPHER_KEY_LEN) {
int numPad = CIPHER_KEY_LEN - key.length();
for(int i = 0; i < numPad; i++){
key += "0"; //0 pad to len 16 bytes
}
} else if (key.length() >CIPHER_KEY_LEN) {
key = key.substring(0, CIPHER_KEY_LEN); //truncate to 16 bytes
}
IvParameterSpec initVector = new IvParameterSpec(iv.getBytes("ISO-8859-1"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("ISO-8859-1"), "AES");
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, initVector);
byte[] encryptedData = cipher.doFinal((data.getBytes()));
String base64_EncryptedData = Base64.getEncoder().encodeToString(encryptedData);
String base64_IV = Base64.getEncoder().encodeToString(iv.getBytes("ISO-8859-1"));
return base64_EncryptedData + ":" + base64_IV;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
decrypt method
private String decrypt(String key, String data) {
try {
if (key.length() < CIPHER_KEY_LEN) {
int numPad = CIPHER_KEY_LEN - key.length();
for(int i = 0; i < numPad; i++){
key += "0"; //0 pad to len 16 bytes
}
} else if (key.length() > CIPHER_KEY_LEN) {
key = key.substring(0, CIPHER_KEY_LEN); //truncate to 16 bytes
}
String[] parts = data.split(":");
IvParameterSpec iv = new IvParameterSpec(Base64.getDecoder().decode(parts[1]));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("ISO-8859-1"), "AES");
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] decodedEncryptedData = Base64.getDecoder().decode(parts[0]);
byte[] original = cipher.doFinal(decodedEncryptedData);
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
I have used
this is the solution i made but seems it not working cause i encrypted this using a java code and using node's javascript code to decrypt it.
function decrypt (messagebase64, keyBase64, ivBase64) {
var key = Buffer.from(keyBase64, 'base64');
var iv = Buffer.from(ivBase64, 'base64');
var decipher = crypto.createDecipheriv(getAlgorithm(keyBase64), key, iv);
decipher.setAutoPadding(false);
decipher.update(messagebase64, 'base64');
return decipher.final();
}
find the alogorithm i use this and adding padding if the key is not long enough but this give error that saying length is not enough.
function getAlgorithm(keyBase64) {
if(keyBase64.length<CIPHER_KEY_LEN){
var padding = CIPHER_KEY_LEN-keyBase64.length;
for(var i=0;i<padding;i++){
keyBase64+="0";
}
}else if(keyBase64.length>CIPHER_KEY_LEN){
keyBase64 =keyBase64.substring(0, CIPHER_KEY_LEN)
}
var key = Buffer.from(keyBase64, 'base64');
switch (key.length) {
case 16:
return 'aes-128-cbc';
case 32:
return 'aes-256-cbc';
}
throw new Error('Invalid key length: ' + key.length);
}
after struggling for a while i have implemented encrypted method and decrypt method which will be identical to java.
decrypt method
function decrypt (messagebase64, keyBase64, ivBase64) {
if(keyBase64.length<CIPHER_KEY_LEN){
var padding = CIPHER_KEY_LEN-keyBase64.length;
for(var i=0;i<padding;i++){
keyBase64+="0";
}
}else if(keyBase64.length>CIPHER_KEY_LEN){
keyBase64 =keyBase64.substring(0, CIPHER_KEY_LEN)
}
var key = Buffer.from(keyBase64, 'latin1');
var iv = Buffer.from(ivBase64, 'base64');
var encryptdata = new Buffer(messagebase64, 'base64');
var decipher = crypto.createDecipheriv('aes-128-cbc', key, iv),
decoded = decipher.update(encryptdata, 'base64', 'utf8');
decoded += decipher.final('utf8');
return decoded
}
encrypt method
function encrypt(plainText, keyBase64, ivBase64) {
if(keyBase64.length<CIPHER_KEY_LEN){
var padding = CIPHER_KEY_LEN-keyBase64.length;
for(var i=0;i<padding;i++){
keyBase64+="0";
}
}else if(keyBase64.length>CIPHER_KEY_LEN){
keyBase64 =keyBase64.substring(0, CIPHER_KEY_LEN)
}
var key = Buffer.from(keyBase64, 'latin1');
var iv = Buffer.from(ivBase64,'latin1');
var encoded_base64_iv= iv.toString('base64');
var cipher2 = crypto.createCipheriv('aes-128-cbc', key, iv);
cipher2.write(plainText);
cipher2.end();
var cipher_text = cipher2.read();
var encodedString = cipher_text.toString('base64');
var final_encrypted_data = encodedString+":"+encoded_base64_iv;
return final_encrypted_data.toString();
};

How to decrypt AES with CryptoJS

I'm trying for some time to decrypt a message in AES that use a Java app , but it never works . Can someone help me?
var options = { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 };
/*** encrypt */
var json = CryptoJS.AES.encrypt("Message", "KEY", options);
var ciphertext = json.ciphertext.toString(CryptoJS.enc.Base64);
console.log("chiper text ", ciphertext);
/*** decrypt */
var decrypted = CryptoJS.AES.decrypt(json, "KEY", options);
var plaintext = decrypted.toString(CryptoJS.enc.Utf8);
console.log("decrypted ", plaintext);
But it is always generated a different ciphertext, never the same from my database.
var CryptoJS = require("crypto-js");
var key = CryptoJS.enc.Utf8.parse('b75524255a7f54d2726a951bb39204df');
var iv = CryptoJS.enc.Utf8.parse('1583288699248111');
var text = "My Name Is Nghĩa";
var encryptedCP = CryptoJS.AES.encrypt(text, key, { iv: iv });
var decryptedWA = CryptoJS.AES.decrypt(encryptedCP, key, { iv: iv});
var cryptText = encryptedCP.toString();
console.log(cryptText);
console.log(decryptedWA.toString(CryptoJS.enc.Utf8));
//Decode from text
var cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: CryptoJS.enc.Base64.parse(cryptText )
});
var decryptedFromText = CryptoJS.AES.decrypt(cipherParams, key, { iv: iv});
console.log(decryptedFromText.toString(CryptoJS.enc.Utf8));
try this to encrypt data
var data = CryptoJS.AES.encrypt(message, key);
data = data.toString()
then decrypt it like this
var decr = CryptoJS.AES.decrypt(data, key);
decr = decr.toString(CryptoJS.enc.Utf8);

CryptoJS Aes decryption in php not working

I am asking this question again, because my last question wasn't answered properly
I am making a CMS, and i have made an encryption system(using cryptoJs), to provide a bit more security to website owners that don't use ssl or tls. But when i decrypt the code in php i get this:
éû*ö^ÿçÿ/Œæ”‰0äU
I have tried to see if it is hex, but when i tried transitioning it from hex to UTF-8, it gave me only gibrish as well.
My system works this way: every time a user goes onto a page, two random strings that are each 100 characters long are created. These strings are adk(Aes decryption key) and keyT. The adk is the secret pass phrase of the key, and the keyT will be the name of the cookie used to store the key.
Random string creation script(PHP):
$characters = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm0123456789";
$charactersLength = strlen($characters);
$adk = "";
for($i=0;$i<100;$i++)
{
$adk .= $characters[rand(0, $charactersLength - 1)];
}
$_SESSION['adk'] = $adk;
$characters2 = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm0123456789";
$charactersLength2 = strlen($characters);
$keyT = "";
for($i=0;$i<100;$i++)
{
$keyT .= $characters2[rand(0, $charactersLength2 - 1)];
}
$_SESSION['keyT'] = $keyT;
I am also using a CryptoJS extension, to create cryptographically safe randoms, instead of using Math.random()
CryptoJS Extension(Javascript):
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 artjomb
*/
(function(C){
var WordArray = C.lib.WordArray;
var crypto = window.crypto;
var TypedArray = Int32Array;
if (TypedArray && crypto && crypto.getRandomValues) {
WordArray.random = function(nBytes){
var array = new TypedArray(Math.ceil(nBytes / 4));
crypto.getRandomValues(array);
return new WordArray.init(
[].map.call(array, function(word){
return word
}),
nBytes
);
};
} else {
console.log("No cryptographically secure randomness source available");
}
})(CryptoJS);
Password encryption script(Javascript&PHP):
function savePassword()
{
password = document.getElementById("ap").value;
var salt = CryptoJS.lib.WordArray.random(128/8);
var iv = CryptoJS.lib.WordArray.random(128/8);
var key = CryptoJS.PBKDF2(<?php echo '"'.$_SESSION['adk'].'"'; ?>, salt, { keySize: 256/32, iterations: 900 });
password = CryptoJS.AES.encrypt(password, key, { iv: iv });
var pB64 = password.ciphertext.toString(CryptoJS.enc.Base64);
var ivB64 = password.iv.toString(CryptoJS.enc.Base64);
var kB64 = password.key.toString(CryptoJS.enc.Base64);
document.cookie=<?php echo '"'.$_SESSION['keyT'].'="'; ?> + kB64 + "; path=/";
document.cookie="encrIv=" + ivB64 + "; path=/";
$(document).ready(function()
{
$("#ap").slideToggle("slow");
$("#sp").slideToggle("slow");
$("#tempBr").remove();
$("#apText").slideToggle("slow");
$("#nb").slideToggle("slow");
$("#sp").remove();
$("#ap").text(pB64);
});
}
The iv and key are put into cookies, but the password will be posted to the next page in a form, where it will be stored into a session.
Form textbox(HTML):
<input type="password" id="ap" name="ap" class="textbox" placeholder="Administrator password" />
Encrypted password storing script(PHP):
$ap = $_POST['ap'];
include_once("../scripts/session_start.php");
$_SESSION['ap'] = $ap;
Decryption script(PHP):
<?php
include_once("scripts/session_start.php");
$keyT = $_SESSION['keyT'];
$toDecrypt = $_SESSION['ap'];
$iv = $_COOKIE['encrIv'];
$key = $_COOKIE[$keyT];
$toDecrypt = base64_decode($toDecrypt);
$iv = base64_decode($iv);
$key = base64_decode($key);
$decrypted = rtrim( mcrypt_decrypt( MCRYPT_RIJNDAEL_128, $key, $toDecrypt, MCRYPT_MODE_CBC, $iv ), "\t\0" );
echo $decrypted;
?>
I am sorry for this hurried answer - I'll clean it up later tomorrow - but the long and the short of it is, getting CryptoJS and PHP to work together wasn't easy.
First of all you have to get the right AES mode (they aren't the same between GoogleCode CryptoJS and PHP mcrypt), then there are quirks with padding and IV.
The code below has been working for some eighteen months now. PHP and JS code jumbled together.
public static function aesEncrypt($password, $plaintext, $js = true) {
$key = self::getAesKey($password, !$js);
// Build $iv and $iv_base64.
// We use a block size of 128 bits (AES compliant) and CBC mode.
// (Note: ECB mode is inadequate as IV is not used.)
if (!function_exists('mcrypt_create_iv')) {
die('FATAL: php5-mcrypt package does not seem to be installed');
}
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_DEV_URANDOM);
return base64_encode(
$iv // S a l t e d _ _
. (true === $js ? hex2bin('53616c7465645f5f0000000000000000') : '')
. mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
$key,
// Add a payload of 32 spaces.
str_repeat(' ', true === $js ? 32 : 0) . $plaintext,
MCRYPT_MODE_CBC,
$iv
)
);
$encrypt = Crypto::aesEncrypt(
PASSWORD
MESSAGE
);
<script src="/static/js/aes.js"></script>
<script src="/static/js/sha256.js"></script>
<script>
// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob('{$encrypt}'); // Base64, IV plus naked ciphertext
var iv = CryptoJS.enc.Latin1.parse(rawData.substring(0, 16));
var encrypt = CryptoJS.enc.Latin1.parse(rawData.substring(16));
document.getElementById('password').onkeydown = function(e) {
if (13 === e.keyCode) {
decrypt();
}
};
function hex2a(hex) {
var str = '';
for (var i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}
function decrypt() {
var passw = document.getElementById('password').value;
var key = CryptoJS.SHA256(passw);
var plain = CryptoJS.AES.decrypt({ ciphertext: encrypt }, key, { iv: iv });
// Skip first 16 + 32 bytes of decrypted text.
document.getElementById('details').innerHTML = hex2a(plain.toString().substr(96));
}
I found a workaround, the encrypted password will be stored into a cookie, to be decrypted by cryptoJS later.

Encryption in JavaScript and decryption with PHP

I'm encrypting my user password in JavaScript like this:
var encryptedPassword = CryptoJS.AES.encrypt(password, "Secret Passphrase");
It works fine but now I'm trying to decrypt in PHP on the server side like this:
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
$decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, "Secret Passphrase", base64_decode($password), MCRYPT_MODE_CBC, $iv);
it doesn't works at all, the decrypted password string looks very strange:
string(64) ">�OX2MS��댗v�<$�ʕ��i�̄��_��P���\�կ=�_6(�m����,4WT7��a"
Here is the current state of my code in JavaScript after the helpful comments:
var encryptedPassword = CryptoJS.AES.encrypt(password, "Secret Passphrase");
var ivHex = encryptedPassword.iv.toString();
var ivSize = encryptedPassword.algorithm.ivSize; // same as blockSize
var keySize = encryptedPassword.algorithm.keySize;
var keyHex = encryptedPassword.key.toString();
var saltHex = encryptedPassword.salt.toString(); // must be sent
var openSslFormattedCipherTextString = encryptedPassword.toString(); // not used
var cipherTextHex = encryptedPassword.ciphertext.toString(); // must be sent
I am sending saltHex and CipherTextHex to the PHP server and I'm using mcrypt_decrypt() like this:
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), $saltHex);
$decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, "Secret Passphrase", base64_decode($cipherTextHex), MCRYPT_MODE_CBC, $iv);
It still does't work with this updated code.
Can someone help me to decrypt properly with mcrypt_decrypt() PHP function for a simple AES encryption method ? I'm sure I am doing something wrong with the cipher, mcrypt mode and the IV parameters inside my mcrypt_decrypt() method. Thanks if you know.
The problem is that in the CryptoJS code a password is used to derive the key and the IV to be used for AES encryption, but mcrypt only uses the key to encrypt/decrypt. This information needs to be passed to php. Since you don't want to transmit the password, you have to derive the key and IV in the same way in php.
The following code derives the key and IV from a password and salt. It is modeled after the code in my answer here (for more information).
function evpKDF($password, $salt, $keySize = 8, $ivSize = 4, $iterations = 1, $hashAlgorithm = "md5") {
$targetKeySize = $keySize + $ivSize;
$derivedBytes = "";
$numberOfDerivedWords = 0;
$block = NULL;
$hasher = hash_init($hashAlgorithm);
while ($numberOfDerivedWords < $targetKeySize) {
if ($block != NULL) {
hash_update($hasher, $block);
}
hash_update($hasher, $password);
hash_update($hasher, $salt);
$block = hash_final($hasher, TRUE);
$hasher = hash_init($hashAlgorithm);
// Iterations
for ($i = 1; $i < $iterations; $i++) {
hash_update($hasher, $block);
$block = hash_final($hasher, TRUE);
$hasher = hash_init($hashAlgorithm);
}
$derivedBytes .= substr($block, 0, min(strlen($block), ($targetKeySize - $numberOfDerivedWords) * 4));
$numberOfDerivedWords += strlen($block)/4;
}
return array(
"key" => substr($derivedBytes, 0, $keySize * 4),
"iv" => substr($derivedBytes, $keySize * 4, $ivSize * 4)
);
}
The salt is generated during encryption in CryptoJS and needs to be sent to php with the ciphertext. Before invoking evpKDF the salt has to be converted to a binary string from hex.
$keyAndIV = evpKDF("Secret Passphrase", hex2bin($saltHex));
$decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128,
$keyAndIV["key"],
hex2bin($cipherTextHex),
MCRYPT_MODE_CBC,
$keyAndIV["iv"]);
If only encryptedPassword.toString() was sent to the server, then it is necessary to split the salt and actual ciphertext before use. The format is a proprietary OpenSSL-compatible format with the first 8 bytes being "Salted__", the next 8 bytes being the random salt and the rest is the actual ciphertext. Everything together is Base64-encoded.
function decrypt($ciphertext, $password) {
$ciphertext = base64_decode($ciphertext);
if (substr($ciphertext, 0, 8) != "Salted__") {
return false;
}
$salt = substr($ciphertext, 8, 8);
$keyAndIV = evpKDF($password, $salt);
$decryptPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128,
$keyAndIV["key"],
substr($ciphertext, 16),
MCRYPT_MODE_CBC,
$keyAndIV["iv"]);
// unpad (PKCS#7)
return substr($decryptPassword, 0, strlen($decryptPassword) - ord($decryptPassword[strlen($decryptPassword)-1]));
}
The same can be achieved with the OpenSSL extension instead of Mcrypt:
function decrypt($ciphertext, $password) {
$ciphertext = base64_decode($ciphertext);
if (substr($ciphertext, 0, 8) != "Salted__") {
return false;
}
$salt = substr($ciphertext, 8, 8);
$keyAndIV = evpKDF($password, $salt);
$decryptPassword = openssl_decrypt(
substr($ciphertext, 16),
"aes-256-cbc",
$keyAndIV["key"],
OPENSSL_RAW_DATA, // base64 was already decoded
$keyAndIV["iv"]);
return $decryptPassword;
}
/**
*-------------PHP code example-----------------
*/
/**
* Decrypt data from a CryptoJS json encoding string
*
* #param mixed $passphrase
* #param mixed $jsonString
* #return mixed
*/
function cryptoJsAesDecrypt($passphrase, $jsonString){
$jsondata = json_decode($jsonString, true);
$salt = hex2bin($jsondata["s"]);
$ct = base64_decode($jsondata["ct"]);
$iv = hex2bin($jsondata["iv"]);
$concatedPassphrase = $passphrase.$salt;
$md5 = array();
$md5[0] = md5($concatedPassphrase, true);
$result = $md5[0];
for ($i = 1; $i < 3; $i++) {
$md5[$i] = md5($md5[$i - 1].$concatedPassphrase, true);
$result .= $md5[$i];
}
$key = substr($result, 0, 32);
$data = openssl_decrypt($ct, 'aes-256-cbc', $key, true, $iv);
return json_decode($data, true);
}
/**
* Encrypt value to a cryptojs compatiable json encoding string
*
* #param mixed $passphrase
* #param mixed $value
* #return string
*/
function cryptoJsAesEncrypt($passphrase, $value){
$salt = openssl_random_pseudo_bytes(8);
$salted = '';
$dx = '';
while (strlen($salted) < 48) {
$dx = md5($dx.$passphrase.$salt, true);
$salted .= $dx;
}
$key = substr($salted, 0, 32);
$iv = substr($salted, 32,16);
$encrypted_data = openssl_encrypt(json_encode($value), 'aes-256-cbc', $key, true, $iv);
$data = array("ct" => base64_encode($encrypted_data), "iv" => bin2hex($iv), "s" => bin2hex($salt));
return json_encode($data);
}
$encrypted = '{"ct":"nPfd1U0y9o2hRCdwJK6XkM1E01wa1ZjMu3eAzGjUD60=","iv":"2abda27fc571cf74e6efc1ba564801f9","s":"813a340e805f54ae"}';
$key = "123456";
$decrypted = cryptoJsAesDecrypt($key, $encrypted);
/* -------------Javascript code example-----------------*/
var CryptoJSAesJson = {
stringify: function (cipherParams) {
var j = {ct: cipherParams.ciphertext.toString(CryptoJS.enc.Base64)};
if (cipherParams.iv) j.iv = cipherParams.iv.toString();
if (cipherParams.salt) j.s = cipherParams.salt.toString();
return JSON.stringify(j);
},
parse: function (jsonStr) {
var j = JSON.parse(jsonStr);
var cipherParams = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Base64.parse(j.ct)});
if (j.iv) cipherParams.iv = CryptoJS.enc.Hex.parse(j.iv)
if (j.s) cipherParams.salt = CryptoJS.enc.Hex.parse(j.s)
return cipherParams;
}
}
var key = "123456";
var encrypted = CryptoJS.AES.encrypt(JSON.stringify("value to encrypt"), key, {format: CryptoJSAesJson}).toString();
console.log(encrypted);
var decrypted = JSON.parse(CryptoJS.AES.decrypt(encrypted, key, {format: CryptoJSAesJson}).toString(CryptoJS.enc.Utf8));
console.log("decryyepted: "+decrypted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
You can't decrypt with a random initialisation vector - you need to use the same IV the data was encrypted with. Also, IIRC, AES defaults to an 8 bit representation of the encrypted data which will need to be handled carefully in transfer over HTTP.

Categories