Obtain identical encryption results with CryptoJS (JavaScript) and OpenSSL (PHP)

I am in the process of integrating a PHP encryption function into a ReactJS application. I have a requirement to transmit the token in a specific format that was generated using the OpenSSL library function (openssl_encrypt).

It has been observed that the PHP function generates a slightly shorter string compared to the JAVASCRIPT function, but both functions maintain the same attributes and properties.

PHP:

protected static function encrypt($stringData) {
  $encrypted = false;
  $encrypt_method = 'AES-256-CBC';
  $iv = substr(hash('sha256', static::$ivMessage), 0, 16);
  $encrypted= openssl_encrypt($stringData, $encrypt_method, static::$apiSecret, 0, $iv);

  return $encrypted;
}

JAVASCRIPT:

export const encrypt = (stringData) => {
  const iv = CryptoJS.SHA256(IV_MESSAGE).toString(CryptoJS.enc.Hex).substring(0, 16);
  const encrypted = CryptoJS.AES.encrypt(stringData, API_SECRET, {
    iv,
    mode: CryptoJS.mode.CBC,
    pad: CryptoJS.pad.ZeroPadding,
  });

  return encrypted;
};

Sample constants:

const stringData = "{"uid":19,"price":10000000,"duration":240,"credit_purpose":5,"new_tab":false,"cssFile":"kalkulatorok","css":[],"supported":false,"email":"<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="afdbcadcdbefdbcadcdb81c7da">[email protected]</a>","productType":"home_loan","method":"calculator","calculatorType":"calculator","unique":true}";
const IV_MESSAGE = "a";
const API_SECRET = "secret_key"; 

(same for PHP function --> $stringData, $ivMessage; $apiSecret)

How can I replicate the PHP function in JavaScript successfully? What steps should I take next?

Answer №1

To achieve the encryption of PHP code in CryptoJS, certain modifications need to be made to the CryptoJS code:

  • The key must be passed as a WordArray. Passing it as a string will result in interpretation as a passphrase from which a 32-byte key is derived.
  • PHP pads keys that are too short with 0x00 values up to the specified length. On the other hand, CryptoJS does not do this and may use undefined round numbers for AES due to a bug when invalid keys are used, leading to mismatched ciphertext.
  • PKCS7 padding is utilized in the PHP code (see comment). This padding must also be included in the CryptoJS code, where it is the default along with the CBC mode.

The provided PHP code snippet:

function encrypt($stringData) {
    
  $ivMessage = "a";
  $apiSecret = "secret_key"; 

  $encrypted = false;
  $encrypt_method = 'AES-256-CBC';
  $iv = substr(hash('sha256', $ivMessage), 0, 16);
  $encrypted= openssl_encrypt($stringData, $encrypt_method, $apiSecret, 0, $iv);

  return $encrypted;
}

$stringData = '{"uid":19,"price":10000000,"duration":240,"credit_purpose":5,"new_tab":false,"cssFile":"kalkulatorok","css":[],"supported":false,"email":"<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9de9f8eee9dde9f8eee9b3f5e8">[email protected]</a>","productType":"home_loan","method":"calculator","calculatorType":"calculator","unique":true}';
print(encrypt($stringData) . "\n");

produces the output:

d/H+FfTaT/3tIkaXtIix937p6Df/vlnxagNJGJ7ljj48phT7oA7QssTatL3WNZY0Igt0r5ObGyCt0AR0IccVTFVZdR+nzNe+RmKQEoD4dj0mRkZ7qi/y3bAICRpFkP3Nz42fuILKApRtmZqGLTNO6dwlCbUVvjg59fgh0wCzy15g51G6CYLsEHa89Dt193g4qcXRWFgI9gyY1Gq7FX0G6Ers0fySQjjNcfDJg0Hj5aSxbPU6EPn14eaWqkliNYSMqzKhe0Ev7Y54x2YlUCNQeLZhwWRM2W0N+jGU7W+P/bCtF4Udwv4cweUESXkHLGtlQ0K6O5etVJDtb7ZtdEI/sA==

The following CryptoJS code snippet generates the same ciphertext:

const IV_MESSAGE = "a";
const API_SECRET = "secret_key\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

function encrypt(stringData){
    const iv = CryptoJS.SHA256(IV_MESSAGE).toString(CryptoJS.enc.Hex).substring(0, 16);
    const encrypted = CryptoJS.AES.encrypt(
        stringData, 
        CryptoJS.enc.Utf8.parse(API_SECRET), 
        {
            iv: CryptoJS.enc.Utf8.parse(iv)
        });

    return encrypted;
};

const stringData = {"uid":19,"price":10000000,"duration":240,"credit_purpose":5,"new_tab":false,"cssFile":"kalkulatorok","css":[],"supported":false,"email":"<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="750110060135011006015b1d00">[email protected]</a>","productType":"home_loan","method":"calculator","calculatorType":"calculator","unique":true};
const ciphertextB64 = encrypt(JSON.stringify(stringData)).toString();

console.log(ciphertextB64.replace(/(.{64})/g,'$1\n'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

Additionally, consider the following:

  • It's recommended to avoid encoding the IV as a hex string during IV generation and use binary data directly. Different platforms may apply different upper/lower case conventions for hex numbers, so keep this variation in mind. However, lowercase usage here poses no critical issues.
  • If a passphrase like secret_key is used as the key, it's advisable to employ a secure key derivation function (e.g., PBKDF2 with a randomly generated salt) due to low entropy. Avoid using the default KDF in CryptoJS, OpenSSL's proprietary function EVP_BytesToKey, as it lacks standardization and is considered relatively insecure.
  • For security reasons, static IVs should be avoided. Instead, generate a random IV for each encryption operation. The IV, which is non-secret, is typically concatenated with the ciphertext in the format IV, ciphertext (see comment).

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing JavaScript variables imported from an external library in Next.js: A Guide

I am currently working on a Next.js with Typescript website and I am in the process of adding advertisements. The ad provider has given me instructions to embed this JavaScript code on my site: <script src="//m.servedby-buysellads.com/monetization. ...

Removing Embedded Json Element from a Collection in AngularJS HTML

In my JSON Collection, I want to display the Email ID that is marked as IsPreffered = TRUE using AngularJS HTML without using JavaScript. This is my JSON Collection: { "user" : [ { "Name" : "B. Balamanigandan", "Email": [ ...

Ways to extract the coordinates for slices positioned around the circumference of a pie chart

Currently, I am working on designing a pie chart with D3 using d3.layout.pie(). The image resembles the one displayed below, but without the black dots. These dots were added manually in Photoshop to highlight an issue that I am facing. I am curious about ...

What is the process by which JavaScript evaluates the closing parenthesis?

I'm currently working on a calculator project that involves evaluating expressions like (5+4). The approach I am taking is to pass the pressed buttons into an array and then create a parse tree based on the data in that array. One issue I'm faci ...

React - triggering a router link action before the link is assigned a value

In this scenario, the issue arises because the URL changes before the link receives a value. The state is being received directly through the component but by the time it happens, the component has already been clicked. This results in the value being as ...

What is the purpose of the "Dot" symbol in the "runtimeArgs" property of the launch.json file in Visual Studio Code?

As I opened my Visual Studio Code today, a notification greeted me about a new update. Without hesitation, I went ahead and installed it, assuming it would be like any other update I've had in the past. However, after updating to version 1.22.1, I enc ...

Issue: nodebuffer is incompatible with this platform

Currently, I am attempting to utilize the Shpjs package in order to import a Shape file onto a Leaflet map. According to the Shpjs documentation: shpjs Below is the code snippet I am working with: const [geoData, setGeoData] = useState(null); //stat ...

Monitor the number of ng-repeat items altering within a directive

I am using the angular-sly-carousel directive to display some data. The data is dynamically added as the user scrolls down, so I need to reload the sly carousel options after scrolling. Is there a way to detect when the length of ng-repeat elements change ...

Convert string query into an array in PHP containing keys and values

When passing the given query string into a PHP method searchFilterKeyword=ff&searchFilterDateEntered=07%2F29%2F2019&searchFilterStatus=1&searchFilterQuarantineStatus=&searchFilterSize=&searchFilterKennel=&searchFilterType= How can ...

Is it possible to utilize a single command in Discord.js to send multiple embeds?

Is there a way to create a unique bot in Node.js (using Discord.js) by utilizing Visual Studio Code? This exceptional bot should be capable of responding with various embed messages when given one specific command. I attempted using command handler, but u ...

Implementing OAuth2 in a Microservices architecture to establish a user account

In my current setup, I am utilizing a React Application along with a separate Express API. My goal is to allow users to register through my React app. To do this, I believe the oauth2 flows should follow these steps: Prompt the user for information (suc ...

Why does the value become "Undefined" once it is passed to the controller function?

I am unsure why the console.log function returns "undefined". $scope.onSizeSelected = function(productId, sizeQtyPrice){ console.log('The selected size is: ' + sizeQtyPrice); $scope.updateSelectedProductBySizeSelected(productId ,sizeQtyPrice ...

Using Javascript to retrieve form data from a separate file

I am struggling with my HTML and JavaScript files to collect data. Despite my efforts, the function is not executing properly. Below is the code I have been working on: HTML File : <form class="form-newsletter"> <div class="form-group"> ...

Include a class above the specified element; for instance, apply the class "act" to the "<ul>" element preceding the "li.item1"

Hello there! I need some assistance, kinda like the example here Add class and insert before div. However, what I really want to do is add the class "act" to a class above that matches the one below: Here's how it currently looks: <ul> ...

Reading a Json file with keys in puppeteer BDD: A Guide

Greetings, I am new to the world of puppeteer. I have successfully created my basic framework and now I am trying to figure out how to read data from .json files. I attempted to use the readFile method in my helper class, but unfortunately, it resulted in ...

Ensure that the input remains below ten

My goal here is to ensure that the value in an input element is a non-zero digit (0<x<=9). Here's the HTML tag I'm using: <input type="number" class="cell"> I've experimented with various JavaScript solutions, but so far none h ...

The code below is not working as it should be to redirect to the home page after logging in using Angular. Follow these steps to troubleshoot and properly

When looking at this snippet of code: this.router.navigate(['/login'],{queryParams:{returnUrl:state.url}}); An error is displayed stating that "Property 'url' does not exist on type '(name: string, styles: AnimationStyleMetadata". ...

Navigating through nested objects when processing a JSON stream

I am currently developing a software that needs to handle massive JSON files, so I am considering using a streaming event-driven parser (such as jsonstreamingparser) to avoid loading the entire structure into memory at once. My main concern is the necessar ...

Exploring Angular 10: Managing Two Promises in ngOnInit

I am currently working on integrating the Strava API into my Angular app. To summarize briefly: When a user clicks on a button to connect to Strava They are redirected to Strava for authentication (using PKCE) Strava then redirects back to my app with a ...

Preserving data in input fields even after a page is refreshed

I've been struggling to keep the user-entered values in the additional input fields intact even after the web page is refreshed. If anyone has any suggestions or solutions, I would greatly appreciate your assistance. Currently, I have managed to retai ...