简体   繁体   中英

Achieve same encryption using CryptoJS (JAVASCRIPT) and OpenSSL (PHP)

I would like to implement a PhP encryption function in a ReactJS application. I need to send the token in the specific format which was created with the OpenSSL library function (openssl_encrypt).

The PHP function produces a few character shorter string in comparison to the JAVASCRIPT function. Of course, both get 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":"test@test.hu","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 achieve to "replicate" the PHP function in JAVASCRIPT? What did I miss so far?

The following changes in the CryptoJS code are necessary to generate the ciphertext of the PHP code:

  • The key must be passed as WordArray . If it is passed as a string, it is interpreted as a passphrase from which a 32 bytes key is derived.
  • PHP pads too short keys with 0x00 values up to the specified length. CryptoJS does not do this and (due to a bug ) generally uses undefined round numbers for AES in case of invalid keys, so that no matching ciphertext is to be expected.
  • PKCS7 padding is used in the PHP code (see comment). This must also be applied in CryptoJS code, which however is the default (as well as the CBC mode).

The following PHP code:

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":"test@test.hu","productType":"home_loan","method":"calculator","calculatorType":"calculator","unique":true}';
print(encrypt($stringData) . "\n");

returns the result:

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

The CryptoJS code below 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":"test@test.hu","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>

The following should also be taken into account:

  • It is more reliable to avoid encoding the IV as hex string when generating the IV and to directly use the binary data. Otherwise, you also have to keep in mind that depending on the platform, different upper/lower case of the hex numbers can generally be applied. Here this is not critical, since in both cases lower case is used.
  • If you should really apply a passphrase like secret_key as key, you should also use a reasonable key derivation function (eg PBKDF2 in combination with a randomly generated salt) because of the low entropy. The default KDF used in CryptoJS, the proprietary OpenSSL function EVP_BytesToKey , should not be applied because it is not a standard and is also deemed relatively insecure.
  • For security reasons no static IV may be used. Instead, a randomly generated IV should be applied for each encryption. The IV is not secret and is usually concatenated with the ciphertext in the order IV, ciphertext (see comment).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM