JavaScript Aes Encryption

Javascript AES encryption

JSAES is a powerful implementation of AES in JavaScript.
http://point-at-infinity.org/jsaes/

CryptoJS javascript AES-128, ECB encrypt / decrypt

There are several bugs in your code:

  • Block cipher modes like ECB always require padding if the size of the plaintext does not correspond to an integer multiple of the blocksize, as is the case in the above example. Actually, an exception should be thrown. CryptoJS, however, implicitly pads with 0x00 values, performs the encryption, and truncates the ciphertext to the length of the original plaintext. This processing is haphazard and is (im my opionion) even a bug.

    As fix, padding must be applied. Since you are talking about 0-padding, you may mean zero padding, i.e. CryptoJS.pad.ZeroPadding. Note that PKCS#7 padding (the default) is more reliable.
  • You are using the hex encoder for the key import, so the 16 hex digits of your key are converted to an 8 bytes key, which is not a valid AES key (valid AES key lengths are 16, 24 and 32 bytes). However, due to a bug, CryptoJS processes this key size but produces a non-AES compliant result.

    As fix, for instance, a 32 hex digit key must be used for AES-128 in combination with the Hex encoder. Another alternative that works with the posted key is the Utf8 encoder.
  • The plaintext is obviously not hex encoded, so no hex encoder may be applied.

    As fix the Utf8 encoder is to be used.
  • ECB mode does not use an IV, i.e. the IV is ignored and can therefore be omitted. Note that the ECB mode is insecure precisely because it does not use an IV. Therefore, a mode with an IV should be applied.
  • During decryption CryptoJS.AES.decrypt() expects a CipherParams object, but in the current code the hex encoded ciphertext is passed.

    The fix is to either create a CipherParams object from the hex encoded ciphertext and pass it, or to pass the Base64 encoded ciphertext, which CryptoJS implicitly converts to a CipherParams object.
  • The Utf8 decoding of the decrypted data is missing.

Fixed code:

var text = 'US0378331005-USD-US-en';
var key = '11A1764225B11AA1';

console.log('text:', text);
console.log('key:', key);
console.log('key length:', key.length );

// Fix: Use the Utf8 encoder
text = CryptoJS.enc.Utf8.parse(text);
// Fix: Use the Utf8 encoder (or apply in combination with the hex encoder a 32 hex digit key for AES-128)
key = CryptoJS.enc.Utf8.parse(key);

// Fix: Apply padding (e.g. Zero padding). Note that PKCS#7 padding is more reliable and that ECB is insecure
var encrypted = CryptoJS.AES.encrypt(text, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding });
encrypted = encrypted.ciphertext.toString(CryptoJS.enc.Hex);
console.log('encrypted', encrypted);

// Fix: Pass a CipherParams object (or the Base64 encoded ciphertext)
var decrypted = CryptoJS.AES.decrypt({ciphertext: CryptoJS.enc.Hex.parse(encrypted)}, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding });

// Fix: Utf8 decode the decrypted data
console.log('decrypted', decrypted.toString(CryptoJS.enc.Utf8));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>

Swift CryptoKit AES Encryption and Javascript AES Decryption

CryptoJS uses CBC mode by default, and doesn’t support GCM at all. You shouldn’t use CryptoJS at the best of times (the native and better-designed Web Crypto API is to be preferred), but especially not on the server, where Node.js has always had a native crypto module.

First, include the GCM nonce and tag, which are essential components:

return val.combined!.base64EncodedString()

Then, in Node.js, using the layout as described in the documentation for the combined property:

The data layout of the combined representation is: nonce, ciphertext, then tag.

// where `sealedBox` is a buffer obtained with `Buffer.from(encryptedString, 'base64')`
let nonce = sealedBox.slice(0, 12);
let ciphertext = sealedBox.slice(12, -16);
let tag = sealedBox.slice(-16);
let decipher = crypto.createDecipheriv('aes-128-gcm', key, nonce);
decipher.setAuthTag(tag);
let decrypted =
Buffer.concat([decipher.update(ciphertext), decipher.final()])
.toString('utf8');

Once that’s working, don’t forget to fix your key, because .init(data: Array(key.utf8)) is very uncomfortable (your AES keys should not be valid UTF-8).

  • If you’re starting with a password (for a good reason, not just because it seemed convenient), use a PBKDF to get key bytes. (Unfortunately, no PBKDF implementations are built into CryptoKit.)

    … but if the good reason is that it’s a user-provided password and you’re claiming to provide security, please get someone experienced with use of cryptography to review your work.

  • Otherwise, generate a random key safely and decode it from a Base64 or hex string. No UTF-8.

    node -p 'crypto.randomBytes(16).toString("base64")'

    And get someone experienced with use of cryptography to review your work anyway.

AES encryption using C# and decryption using Javascript

The bugs are in the determination of IV and key. Both are derived from passwords using SHA512. In the case of the key the first 32 bytes (bytes 0-31) are used, in the case of the IV the 16 bytes following the first 32 bytes (bytes 32-47).

The fixed code is:

var ciphertext = "0MuDwNoWBFjN/1anszbl0Cxkrwh9ahRwE3c61t7io2c=";
var key = "8beee7ac-42d1-4294-91b8-68cd032cf1e1";
var iv = "9bC_#$/-+%@Kli%1Az=-@qT";

var ciphertextWA = CryptoJS.enc.Base64.parse(ciphertext);
var ciphertextCP = { ciphertext: ciphertextWA };

var keyHashWA = CryptoJS.SHA512(key);
var keyWA = CryptoJS.lib.WordArray.create(keyHashWA.words.slice(0, 32/4));
var ivHashWA = CryptoJS.SHA512(iv);
var ivWA = CryptoJS.lib.WordArray.create(ivHashWA.words.slice(32/4, 48/4));

var decryptedWA = CryptoJS.AES.decrypt(
ciphertextCP,
keyWA,
{ iv: ivWA }
);
console.log(decryptedWA.toString(CryptoJS.enc.Utf8));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>

What is the difference between AES encryption algorithm and secret key in crypto-js library?

The algorithm is a series of steps that happen in processing the data with the secret key to produce the encrypted data.

There are two inputs into the algorithm - the key and the initial data. The algorithm takes those two inputs and produces the encrypted output.

+---------+  +---------+
| Key | | Data |
+---------+ +---------+
\ /
\ /
\ /
\ /
\ /
+-----------+
| Algorithm |
+-----------+
|
+-----------+
| Encrypted |
| Result |
+-----------+

The key and the data are separate from the algorithm. If you change either of them, you will get a different encrypted result without changing the algorithm.


In your example of the very simple Caesar Cipher, the algorithm is that each character in the input is going to be replaced by another character (a substitution cipher) that is offset in the alphabet by some amount.

The key would be what the amount is. So, if the key is 1, then a is replaced by b and b is replaced by c and so on. The code for the algorithm can be written to accept the key as an input parameter or function argument and the algorithm code does not have to be rewritten for a different key. The key is applied to the input data by the algorithm programmatically to produced the result. The same algorithm code works with all the different keys you can pass it.



Can somebody please tell me if I'm correct [in understanding the algorithm and key in the Caesar Cipher]?

Yes, your understanding of that is correct.

If I'm correct, can you please tell me how exactly this translates in the case of the AES encryption mentioned in the beginning?

The AES encryption is a much more complicated algorithm that again accepts input data and a key. In this case, the key is a block of data itself, not just a single number. If you want to know more about how it works, you can find many articles on the web about it so it's probably better to read those than try to repeat all that here. Here's one article: What is AES Encryption and How Does It Work?.

Note, you generally do not need to know how a given encryption algorithm works in order to use it successfully. You do need to know how secure it is, what kind of keys are required, what kind of output it generates and how you decrypt it. But, you don't need to know the details of how the algorithm works. And, you need to select the right type of algorithm (for example, symmetric encryption with the same key used for encryption and description vs. asymmetric encryption such as public key/private key pairs) because this determines how you generate/manage/share secrets.

In your code example:

const ciphertext = CryptoJS.AES.encrypt('my message', 'secret key 123').toString();

CryptoJS.AES.encrypt is a function that implements the algorithm. It accepts two arguments. The first argument is the data you want encrypted. The second argument is a key string where all the data in the key is used in the encryption and the key will need to be supplied again in order to descrypt the data.

The result of the call to CryptoJS.AES.encrypt() is a buffer of data.

CryptoJS AES encrypt (with key size 128 / 8) equivalent in PHP

As @Topaco pointed out in comments, the key and IV must not be hex decoded, i.e. remove both hex2bin().

The corrected code is given below.

<?php

$customer = [
'CustomerName' => "test",
'EmailID' => "tester@test.com",
'Street' => "Test",
'City' => "London",
'Country' => "United Kingdom",
'ZipCode' => "XXX XXX",
];


$plaintext = json_encode($customer);
$method = 'AES-128-CBC';

$key = "8056483646328123";
$iv = "8056483646328123";

$ciphertext = openssl_encrypt(
$plaintext,
$method,
$key,
OPENSSL_RAW_DATA,
$iv
);


$ciphertext = base64_encode($ciphertext);

echo $ciphertext;

?>


Related Topics



Leave a reply



Submit