8

I am working on the web application using react as front-end and spring mvc as back-end. I need to store some user information in local storage of the browser. I do not want to store that info in local storage as a plain text. So I thought to go for AES encryption at server side and pushing those data back to JS side. For that I need client side decryption framework. I found crypto-js as very useful for all these things. I am not able to understand where I am lacking at client side to decrypt and decode.

I am explaining my Spring Side Encryption Code first which is absolutely fine:

public class EncryptDecrypt {

        private static final String SECRET_KEY_1 = "ssdkF$HUy2A#D%kd";
        private static final String SECRET_KEY_2 = "weJiSEvR5yAC5ftB";

        private IvParameterSpec ivParameterSpec;
        private SecretKeySpec secretKeySpec;
        private Cipher cipher;

        public EncryptDecrypt() throws UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException {
            ivParameterSpec = new IvParameterSpec(SECRET_KEY_1.getBytes("UTF-8"));
            secretKeySpec = new SecretKeySpec(SECRET_KEY_2.getBytes("UTF-8"), "AES");
            cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        }

        public String encrypt(String toBeEncrypt) throws NoSuchPaddingException, NoSuchAlgorithmException, 
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] encrypted = cipher.doFinal(toBeEncrypt.getBytes());
            return Base64.encodeBase64String(encrypted);
        }
}

At the client side, I am not able to decode and decrypt the code with simple things. Here is my client side code:

var CryptoJS = require("crypto-js");

var data = "Ggydx4oA1+SKBw+unA8BUUm2tnvkQbp1terdF2PEGFYSEZL/ye08op/0b0BauGtIl1dBIodrlKXo2de3MykYmocd3ctxFtIIki01V+M8XeQj6B384o0G+H7NpVx5tCJjPDvdqVRObtxCTqu3r8QRzYTNcMM5bRhbYxCYl8/NRyPQJnmcJDlRBeVOoJiQNA7Qd5UJD/mNivoyMUfYGV7/DlpylQWWwEAHVdgcb865i8jnf3vqURehAXYoaD6Bgodi1EM4H007uv0o6NEOk3H4jQ==";

var key = "weJiSEvR5yAC5ftB";

// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = "ssdkF$HUy2A#D%kd";
var crypttext = rawData.substring(16);

console.log(rawData);

// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
  { ciphertext: CryptoJS.enc.Base64.parse(crypttext) },
  key,
  { iv: iv }
);

console.log(plaintextArray);

console.log(CryptoJS.enc.Base64.stringify(plaintextArray));

var decryptedData = JSON.parse(CryptoJS.enc.Base64.stringify(plaintextArray).toString(CryptoJS.enc.Utf8));

console.log(decryptedData);

P.S: I have sent JSON to client side and so that I am parsing it in the end. I am newbie for encryption and decryption. I am really stuck with what my client side code should look a like. Please help.

samkit shah
  • 657
  • 7
  • 18
  • @rhashimoto can you help me with this? – samkit shah Feb 03 '19 at 09:11
  • 1. You need to decode64 before `rawData.substring(16);` 2. The Spring side may prepend the IV to the ciphertext. Compare the size of input and output of Spring Encryption. If they are same then IV has not prepended then whole data is the ciphertext. If the reverse than after decoding you can get the ciphertext bytes. – kelalaka Feb 03 '19 at 13:17
  • @kelalakaThat is what I have done. I have already decoded string before rawData.substring(16); – samkit shah Feb 03 '19 at 22:57
  • Did you check the first 16 bytes of the rawData is IV and if so matches your IV? – kelalaka Feb 04 '19 at 09:57

1 Answers1

6

You shouldn't pass string as key in CryptoJS. In this case it considers this string not as key, but as password. And generate key from password by using PBKDF. Working example below:

var data = "Ggydx4oA1+SKBw+unA8BUUm2tnvkQbp1terdF2PEGFYSEZL/ye08op/0b0BauGtIl1dBIodrlKXo2de3MykYmocd3ctxFtIIki01V+M8XeQj6B384o0G+H7NpVx5tCJjPDvdqVRObtxCTqu3r8QRzYTNcMM5bRhbYxCYl8/NRyPQJnmcJDlRBeVOoJiQNA7Qd5UJD/mNivoyMUfYGV7/DlpylQWWwEAHVdgcb865i8jnf3vqURehAXYoaD6Bgodi1EM4H007uv0o6NEOk3H4jQ==";
var rawData = CryptoJS.enc.Base64.parse(data);
var key = CryptoJS.enc.Latin1.parse("weJiSEvR5yAC5ftB");
var iv = CryptoJS.enc.Latin1.parse("ssdkF$HUy2A#D%kd");
var plaintextData = CryptoJS.AES.decrypt(
    { ciphertext: rawData },
    key,
    { iv: iv });
var plaintext = plaintextData.toString(CryptoJS.enc.Latin1);
console.log(plaintext);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js"></script>

BTW, you shouldn't use the same IV every time. In this case you miss the base purpose of IV and CBC mode. Your overall security becomes equal to ECB mode.

Zergatul
  • 1,957
  • 1
  • 18
  • 28
  • Thank you so much. You are savior. Yes, I will use different IV all the time and will use salt as well. This was just the example data. Thank you so much again. – samkit shah Feb 06 '19 at 18:27
  • Great answer! You can use `CryptoJS.enc.Utf8` if you want to support non-latin characters. – mhrabiee Nov 12 '19 at 15:09
  • @Zergatul do you have idea how in `CryptoJS` to `encrypt()` using `GCM` as the `mode` and `PKCS5Padding` as the `padding`? – Compaq LE2202x Oct 20 '21 at 18:57
  • 1
    @CompaqLE2202x `GCM` doesn't use padding. `GCM` acts like stream cipher, meaning it can work with any message length. Padding is required for block ciphers, when message length must be multiple of something. Technically you can use padding, before use `GCM`, it just doesn't make any sense. – Zergatul Oct 20 '21 at 19:55
  • @Zergatul noted on `GCM` not using padding. What would be the`mode`, any idea? I see here (https://cryptojs.gitbook.io/docs/#block-modes-and-padding) that `GMS` is not listed. Thanks! – Compaq LE2202x Oct 20 '21 at 20:06
  • 1
    @CompaqLE2202x That means CryptoJS doesn't support `GCM` – Zergatul Oct 20 '21 at 20:14