CryptoJS Aes decryption in php not working - javascript

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.

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).

JS CryptoJS encrypted -> PHP OpenSSL decrypted: cipher changes length

I have a problem which I can't understand. I programmed some code to encrypt data in JS with CryptoJS and to decrypt this data in PHP with OpenSSL. Sometimes it work but most of the time it breaks
Code:
PHP JS
$passphrase == passkey
JS:
let data = CryptoJS.AES.encrypt(data, passkey).toString();
send(data);
PHP:
list($cipher, $salt) = decode($_POST["data"]);
list($passkey, $iv) = evpkdf($passphrase, $salt);
$data = openssl_decrypt($cipher, "AES-256-CBC", $passkey, OPENSSL_RAW_DATA, $iv);
echo $data
function evpkdf($passphrase, $salt) {
$salted = "";
$dx = "";
while(strlen($salted) < 48) {
$dx = md5($dx . $passphrase . $salt, true);
$salted .= $dx;
}
$passkey = substr($salted, 0, 32);
$iv = substr($salted, 32, 16);
return [$passkey, $iv];
}
function decode($data) {
$data = base64_decode($data);
if (substr($data, 0, 8) !== "Salted__") {
// throw new \InvalidArgumentException();
echo "no salt\n";
}
$salt = substr($data, 8, 8);
$cipher = substr($data, 16);
return [$cipher, $salt];
}
Results:
PHP JS
iv == iv
salt == salt
cipher ?? cipher
?? = sometimes
The only thing which I noticed was that the Cipher is not always the same length and only if the cipher has the right length it worked. What could be my error which I can't see. I already tried a lot and I really have no idea what to do. I tried to change the passkey in JS or to look if something else is different, but as above only the cipher doesn't make sense all the time.

Encrypt in PHP openssl and decrypt in javascript CryptoJS

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)
}

CryptoJS AES encryption and PHP decryption with cookies

I'm trying to use CryptoJS to encrypt with AES and decrypt using in PHP.
Here is the code I'm using for JS e. All encoded data is written to cookie
Cookie.write = function (key, value, duration) {
value=encrypt(value);
Cookie.remove(key);
var d = new Date();
if (duration <= 0)
duration = 1;
d.setTime(d.getTime() + 1000 * 60 * 60 * 24 * duration);
document.cookie = key + "=" + encodeURI(value) + "; expires=" + d.toGMTString() + ";path=/";
};
function encrypt(word)
{
var key = CryptoJS.enc.Utf8.parse("010216a654976158");
var iv = CryptoJS.enc.Utf8.parse("010216a654976158");
var src = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(src, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
});
return encrypted.toString();
}
function decrypt(word)
{
var key = CryptoJS.enc.Utf8.parse("010216a654976158");
var iv = CryptoJS.enc.Utf8.parse("010216a654976158");
var decrypt = CryptoJS.AES.decrypt(word, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
});
return CryptoJS.enc.Utf8.stringify(decrypt).toString();
}
Now here is the php code that I'm using
$uidE = $_COOKIE["uid"];
$decode = base64_decode($uidE);
$pk ="010216a654976158";
$iv ="010216a654976158";
$value = '4';
$encrypted = ( mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $pk, $value, MCRYPT_MODE_CBC,$iv) );
$uid = (mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $pk, $decode, MCRYPT_MODE_CBC,$iv));
echo base64_encode($encrypted);
This is what I get:
0rHkBQo88rYhLP0qjIBeSQ==
I don't know why but when I use the value of 4 to encode using JS and write
'0rHkBQo88rYhLP0qjIBeSQ==' to cookie I have no problems decoding using php.
If I use any other number, the decoded value of $uid is unreadable,
but if I change $value to that number I can decode no problem.

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