skip to Main Content

I have this bit of code in my js :

export const encrypt = (data, key) => {
    const encrypted = CryptoJS.AES.encrypt(
        JSON.stringify(data),
        key
    ).toString();
    return encrypted;
};

but when i decrypt it in my laravel backend :

Route::post('/decrypt', function (Request $request) {
    try{
        $encrypted = $request->input("data");
        Crypt::decrypt($encrypted);
    }
    catch(DecryptException $e) {
        dd("error", $e);
    }
});

this gives me the DecryptException "The payload is invalid"

2

Answers


  1. Well… the encryption world is extremely tricky, so never take anything for granted. While encrypting/decrypting with the same tool is somewhat trivial, doing the same across different systems may reveal very troublesome.

    Here, assuming all the elements are in place (correct install, correct config…!), the problem might lay in the data or the key.

    Key

    As pointed out by @Kris, you should check if "the key is the same in both sides". That might seem obvious at first glance… but it’s not really so.

    In effect, before encrypting/decrypting, CryptoJS first checks if "key" is a real key or a plaintext password (called passphrase). If the latter is the case, then it converts the plaintext password into a key, and then processes the data. Again… what type of conversion? To a key of what length? It all depends in the configuration you have, and the defaults of your version. Usually, the default key size is 256.

    Now, I don’t see any password setting in your backend example, but you should check if it is handling the key in the same exact way as CryptoJS is doing it. If you have control, you could try to replicate, in the backend, CryptoJS’s exact behavior with the key. But handling the conversion yourself might be a real timesaver, providing CryptoJS with a real key that it’s not going to try to process.

    Data

    Encrypted data is typically binary, and not suitable for transport. There are many forms to transport data safely, and one of the more widely used is BASE64. This is actually what CryptoJS converts the data to, when using toString(), like you do.

    Now, you have to check that your backend is expecting this format, and deals with it correctly, both the interface and the decryption routine. For instance, if Crypt::decrypt() is expecting binary data, you should first BASE64 decode the received data.

    Login or Signup to reply.
  2. It looks like you’re encountering an issue because the encryption methods and libraries you’re using in JavaScript and Laravel are not compatible by default. CryptoJS.AES.encrypt and Crypt::decrypt in Laravel use different default configurations and algorithms. Here are a few points to ensure compatibility between your JavaScript and Laravel encryption methods:

    Consistent Encryption Parameters: Ensure that both JavaScript and Laravel use the same encryption parameters (algorithm, mode, padding, etc.). By default, CryptoJS.AES.encrypt uses CBC mode and PKCS7 padding. Laravel’s Crypt facade uses AES-256-CBC mode and PKCS7 padding as well, but it requires a specific key size and configuration.

    Ensure that the key used in JavaScript matches the key used in Laravel and that you properly manage the IV (Initialization Vector). The CryptoJS.AES.encrypt method may also require specifying an IV. In Laravel, the Crypt facade handles IV internally, but in a custom implementation, you’ll need to manage IV manually.

    Encoding: Make sure that the data is properly encoded and decoded between the systems. CryptoJS outputs Base64-encoded strings, and Laravel expects Base64 encoding as well.

    I think the below should help:

    export const encrypt = (data, key) => {
        // Ensure your key is 32 bytes for AES-256
        const keySize = 256 / 32;
        const keyHex = CryptoJS.enc.Hex.parse(key);
        const iv = CryptoJS.lib.WordArray.random(16); // Random 16 bytes IV
    
        const encrypted = CryptoJS.AES.encrypt(
            JSON.stringify(data),
            keyHex,
            { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }
        );
    
        // Encode both the IV and the encrypted data
        return `${iv.toString(CryptoJS.enc.Base64)}:${encrypted.toString()}`;
    };
    

    Laravel Decryption

    Route::post('/decrypt', function (Request $request) {
        try {
            // Extract the IV and encrypted data
            $data = $request->input('data');
            list($iv, $encrypted) = explode(':', $data, 2);
    
            // Decode the Base64 encoded values
            $iv = base64_decode($iv);
            $encrypted = base64_decode($encrypted);
    
            // Your key should be 32 bytes for AES-256
            $key = env('AES_KEY'); // Ensure this key matches the one used in JavaScript
    
            // Decrypt using openssl
            $decrypted = openssl_decrypt(
                $encrypted,
                'aes-256-cbc',
                $key,
                OPENSSL_RAW_DATA,
                $iv
            );
    
            return response()->json(['data' => json_decode($decrypted, true)]);
        } catch (DecryptException $e) {
            return response()->json(['error' => 'Decryption failed'], 400);
        }
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search