0

I'm using Crypto-js for encrypting password with a key and send it to server. I want to decrypt it in server using PHP. How this can be done?

JS:

  let encKey = "Secret Passphrase"; 
  let text = "123";
  let iv = CryptoJS.enc.Hex.parse("FgLFXEr1MZl2mEnk");
  var encryptedText = CryptoJS.AES.encrypt(text, encKey, { iv: iv }).toString();

Encrypted text:

U2FsdGVkX1+EaW3J1GE1k/EU5h6C+nxBH364Xhez+b0=

PHP:

<?php
$strg  =  "U2FsdGVkX1+EaW3J1GE1k/EU5h6C+nxBH364Xhez+b0=";
$encryptedstrings  =  base64_decode($strg);
$encryptionMethod  =  'aes-256-cbc';
$key  =  "Secret Passphrase";
$iv  =  "FgLFXEr1MZl2mEnk";
  
$rawText   = openssl_decrypt($encryptedstrings, $encryptionMethod, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING , $iv);

var_dump($rawText);

Result:

string(32) "����>���s��ȡ�V?E��M���I"

I'm getting weird results here.

Shyam3089
  • 459
  • 1
  • 5
  • 20
  • You are not passing the `$iv` in. – luk2302 Dec 10 '20 at 12:22
  • I passed the $iv in, still getting wrong data. (Edited the question) – Shyam3089 Dec 10 '20 at 12:25
  • Currently you do not use a _key_ in the CryptoJS code, but a _password_. To use a 32 bytes key (for AES-256) you have to pass a `WordArray`, e.g. `CryptoJS.enc.Utf8.parse("01234567890123456789012345678901")`. Regarding the IV the wrong parser is used. A 16 bytes IV would be possible with the _Utf8_ encoder, e.g. `CryptoJS.enc.Utf8.parse("FgLFXEr1MZl2mEnk")`. In the PHP code PKCS7 padding must be used, i.e. the `OPENSSL_ZERO_PADDING` flag must be removed. – Topaco Dec 10 '20 at 13:04
  • @Topaco when I remove the `OPENSSL_ZERO_PADDING` flag it's returning false. Could you please give an example? – Shyam3089 Dec 10 '20 at 13:22
  • You must not only change the flag, but also fix the other bugs, as described in my comment. If you want to use a key, as phrased in your question, then you must pass the key as a `WordArray`. If you want to use a password, then you have to pass it as a string. I wouldn't recommend the latter though, as an insecure key derivation function is applied. – Topaco Dec 10 '20 at 13:31

1 Answers1

1

The following solution is not from my side but from @Artjom B., so all credits go to him. You will find the source here: https://stackoverflow.com/a/27678978/8166854.

To your problem: you run the CryptoJs encryption with a passphrase and not with a key. According to the docs (https://cryptojs.gitbook.io/docs/#the-cipher-algorithms) section cipher algorithms the (internal AES) key is derived from the passphrase with an outdated and unsecure function that should no longer be in use.

Artjom B. was able to make this key derivation available on PHP. As a side note: it is not necessary to present an initialization vector (IV) to the encryption function as the IV is as well derived from the passphrase, so I'm leaving it out in the following code.

This is the result on PHP-side:

solution for https://stackoverflow.com/questions/65234428/decrypt-crypto-js-encrypted-text-with-key-with-php
string(3) "123"
decryptedtext: 123

This is the code, please obey the warning: This code is provided for achieve compatibility between different programming languages. It is not necessarily fully secure. Its security depends on the complexity and length of the password, because of only one iteration and the use of MD5. I would recommend to use at least a 20 character password with alphanumeric characters which is ideally randomly generated.

<?php

/*
source: https://stackoverflow.com/a/27678978/8166854 author: Artjom B.
Security notice: This code is provided for achieve compatibility between different programming languages.
It is not necessarily fully secure. Its security depends on the complexity and length of the password,
because of only one iteration and the use of MD5. I would recommend to use at least a 20 character password
with alphanumeric characters which is ideally randomly generated.
 */

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

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

echo 'solution for https://stackoverflow.com/questions/65234428/decrypt-crypto-js-encrypted-text-with-key-with-php' . PHP_EOL;
$key  =  "Secret Passphrase";
$strg = "U2FsdGVkX1+EaW3J1GE1k/EU5h6C+nxBH364Xhez+b0=";
$rawText = decrypt($strg, $key);
var_dump($rawText);
echo 'decryptedtext: ' . $rawText . PHP_EOL;
?>
Michael Fehr
  • 5,827
  • 2
  • 19
  • 40