AES decryption with password using CryptoJS returns a blank value
Asked Answered
M

4

9

Scenario

I've got the following code:

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<div id="decrypted">Please wait...</div>
Insert new note:<input type="text" id="new_note"><input type="button" id="enc_button" value="Save">
<script>
    var password = "testpassword";
    var encrypted_text = localStorage.getItem("encrypted");
    var rawData = atob(encrypted_text);
    var iv = rawData.substring(0,16);
    var crypttext = rawData.substring(16);
    var plaintextArray = CryptoJS.AES.decrypt(
      { ciphertext: CryptoJS.enc.Latin1.parse(crypttext) },
      CryptoJS.enc.Hex.parse(password),
      { iv: CryptoJS.enc.Latin1.parse(iv) }
    );
    var decrypted = CryptoJS.enc.Latin1.stringify(plaintextArray);
    document.getElementById("decrypted").innerHTML = decrypted;
    document.getElementById("enc_button").onclick = function(){
    	var text = document.getElementById("new_note").value;
    	var encrypted = CryptoJS.AES.encrypt(text, password);
    	localStorage.setItem("encrypted",encrypted);
    }
</script>

What my code should do

Encrypt a string with AES using CryptoJS; decrypt encrypted text saved in local storage and show the result in a div

What is not working

While the string seems to get encrypted, the variable decrypt is empty. No errors are triggered in the chrome console.

My question

How can I successfully encrypt and decrypt my text?

Monocyte answered 8/9, 2015 at 18:54 Comment(0)
F
10

CryptoJS has two slightly different types of encryption/decryption.

When you use

var encrypted = CryptoJS.AES.encrypt(text, password);

then you're using password-based encryption which is not the same as pure key/IV-based encryption. This means that the password and a randomly generated salt are run through one MD5 invocation to produce the key and IV for the actual encryption. This is an OpenSSL compatible way to encrypt something. The encrypted object stores the random salt which was used to generate the key and IV.

When you force encrypted to be converted to string (like adding it to localStorage), then it is converted into an OpenSSL compatible string encoding which includes the salt. In order to decrypt it again, you don't need to mess around with the key, IV or salt yourself, because CryptoJS automatically does that for you:

var decrypted = CryptoJS.AES.decrypt(encrypted, password);

Keep in mind that decrypted is a WordArray object and when you force it to be converted to string it will encode the contents into Hex by default. If you don't want that then you need to specify the encoding such as UTF-8 yourself.

A blank value is usually returned when the decryption failed for some reason such as wrong key, wrong ciphertext or wrong encoding. CryptoJS won't throw custom error messages, but will try to continue, because you should know what you're doing.

Full code:

var password = "testpassword";

document.getElementById("enc_button").onclick = function(){
  var text = document.getElementById("new_note").value;
  
  var encrypted = CryptoJS.AES.encrypt(text, password);
  encrypted = encrypted.toString();
  
  var decrypted = CryptoJS.AES.decrypt(encrypted, password);
  decrypted = decrypted.toString(CryptoJS.enc.Utf8)
  
  document.getElementById("decrypted").innerHTML = decrypted;
}
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
<div id="decrypted">Please wait...</div>
Insert new note:<input type="text" id="new_note"><input type="button" id="enc_button" value="Encrypt & Decrypt">
Farro answered 8/9, 2015 at 19:13 Comment(1)
I removed the localStorage code, because it only obfuscates the issue.Farro
E
0

I tired debugging your code by placing the script in try-catch and looks like document.getElementById is returned undefined. This is because the method is being called before the element is being created. The following steps make this work:

Head:

var password = "testpassword";
var encrypted_text = localStorage.getItem("encrypted");
var rawData = atob(encrypted_text);
var iv = rawData.substring(0,16);
var crypttext = rawData.substring(16);
var plaintextArray = CryptoJS.AES.decrypt(
  { ciphertext: CryptoJS.enc.Latin1.parse(crypttext) },
  CryptoJS.enc.Hex.parse(password),
  { iv: CryptoJS.enc.Latin1.parse(iv) }
);
var decrypted = CryptoJS.enc.Latin1.stringify(plaintextArray);

function setComponents() {
    try {
        document.getElementById("decrypted").innerHTML = decrypted;
        document.getElementById("enc_button").onclick = function(){
            var text = document.getElementById("new_note").value;
            var encrypted = CryptoJS.AES.encrypt(text, password);
            localStorage.setItem("encrypted",encrypted);
            alert(encrypted);
        };
    } catch(e) {
        alert(e);
    }
}

Body:

<body onload="setComponents()">
<div id="decrypted">Please wait...</div>
Insert new note:<input type="text" id="new_note"/><input type="button" id="enc_button" value="Save"/>
</body>
Eructate answered 8/9, 2015 at 19:18 Comment(1)
Thanks for your answer, but the problem was the one that Artjom B. foundMonocyte
N
0

For anyone who had the problem while building an api, GO CHECK THE BODY U ARE SENDING TO TEST MAKE SURE THE BODY OBJECT PROPERTIES ARE WELL NAMED xD took me 1h to realize that

Nugget answered 5/1, 2023 at 23:44 Comment(1)
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewMathur
V
-2

You could just check the source code... there are examples.
Any way the easiest way to make it generate key, iv and salt from passphrase,
is the normal way CryptoJS.AES.encrypt("attack at dawn!","passphrase")
But you can set the parameters before by using for example:

CryptoJS.algo.AES.keySize=32
CryptoJS.algo.AES.blockSize=8
CryptoJS.algo.AES.ivSize=16
CryptoJS.algo.EvpKDF.cfg.iterations=100
CryptoJS.algo.EvpKDF.cfg.keySize=32

etc etc

Volunteer answered 30/6, 2018 at 9:10 Comment(1)
CryptoJS.algo.AES.blockSize=8 is wrong, AES has a block size of 16-bytes—always. Also the IV size is the same as the block size, always 16 bytes, there is no need to set either of these values for AES. 100 iterations of EvpKDF.cfg.iterations may or may not be correct, the aiming point is 100ms of CPU time.Parasitic

© 2022 - 2024 — McMap. All rights reserved.