8

I need to implement AES encryption using JavaScript. Used AES/CBC/NoPadding Mode and created a method to complete 16 lenght blocks. I already solved it using Java. It looks like:

public static String encrypt(byte[] key, byte[] initVector, String value) {
    try {
        IvParameterSpec iv = new IvParameterSpec(initVector);
        SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        byte[] encrypted = cipher.doFinal(completeBlocks(value));
        return Base64.encodeBase64String(encrypted);
    } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException ex) {
        System.out.println("Error: " + ex);
    }

    return null;
}

/**
 * Completes 16 lenght blocks
 *
 * @param message
 *
 *
 */
static byte[] completeBlocks(String message) {
    try {

        int bytesLenght = message.getBytes("UTF-8").length;
        if (bytesLenght % 16 != 0) {
            byte[] newArray = new byte[bytesLenght + (16 - (bytesLenght % 16))];
            System.arraycopy(message.getBytes(), 0, newArray, 0, bytesLenght);
            return newArray;
        }

        return message.getBytes("UTF-8");

    } catch (UnsupportedEncodingException ex) {
        System.out.println("" + ex);
    }
    return null;
}

public static void main(String[] args) {

    String key = "253D3FB468A0E24677C28A624BE0F939";
    String strToEncrypt = "My Secret text";
    final byte[] initVector = new byte[16];
    String resultado = encrypt(new BigInteger(key, 16).toByteArray(), initVector, strToEncrypt.trim());
    System.out.println("ENCRYPTED:");
    System.out.println(resultado);
}

With inputs key = 253D3FB468A0E24677C28A624BE0F939, strToEncrypt = "My Secret text" and ceros IV. It throws

7StScX3LnPUly/VNzBes0w==

I Know it's the desired output. It's right! I tried to replicate this using JavaScript. I used CryptoJs library. But i wasn't able to produce same Java's output. I have tryed:

var text = "My Secret text";
var key = CryptoJS.enc.Base64.parse("253D3FB468A0E24677C28A624BE0F939");
var iv  = CryptoJS.enc.Base64.parse("                ");
var encrypted = CryptoJS.AES.encrypt(text, key, {iv: iv});
console.log(encrypted.toString());

var decrypted = CryptoJS.AES.decrypt(encrypted, key, {iv: iv});
console.log(decrypted.toString(CryptoJS.enc.Utf8));

Using the same inputs, i get De+CvPVIyiBX2//EE6gXTg== as output. What am I doing wrong? How can i get same Java's output? Thanks a lot!!

Sergio David Romero
  • 236
  • 2
  • 6
  • 16
  • It looks cryptojs doesn't support CBC mode. – Tamas Hegedus Jan 16 '17 at 14:36
  • Also, your key is given as hex and not base64, and giving spaces to base64 is not the same as an array of zeroes. – Tamas Hegedus Jan 16 '17 at 14:37
  • @TamasHegedus I think you're right!! It seems sadly CBC mode is not supported. I found this one: https://github.com/ricmoo/aes-js . Looks like it supports CBC mode. I am going to try that!! Thanks a lot! – Sergio David Romero Jan 16 '17 at 14:47
  • 1
    Sergio, it turns out it does support CBC. It's implemented in https://github.com/sytelus/CryptoJS/blob/1d75606113b9446ee2bb56130709927eb3b3c45d/components/cipher-core.js , not in mode-cbc.js as some other modes. – Tamas Hegedus Jan 16 '17 at 14:51
  • @TamasHegedus I'm checking it!! U looks great! I'm going to try that! Thanks – Sergio David Romero Jan 16 '17 at 16:01

2 Answers2

15

Assuming you will fix things like the empty IV & that this is a proof of concept, your code fails because:

  1. You use no padding in Java, you need to use the same in JS
  2. You manually pad with nulls in Java, you need to do the same in JS
  3. You base64 decode the key but its not base64 (its a hexadecimal string of bytes)
  4. The Java IV is an array of nulls but in JS you use whitespace (and erroneously treat it as base64).

To duplicate the output in JS:

CryptoJS.pad.NoPadding = {pad: function(){}, unpad: function(){}};

var text = "My Secret text\0\0";
var key  = CryptoJS.enc.Hex.parse("253D3FB468A0E24677C28A624BE0F939");
var iv   = CryptoJS.enc.Hex.parse("00000000000000000000000000000000");

var encrypted = CryptoJS.AES.encrypt(text, key, {iv: iv, padding: CryptoJS.pad.NoPadding});

console.log(encrypted.toString());

For:

7StScX3LnPUly/VNzBes0w==

Alex K.
  • 171,639
  • 30
  • 264
  • 288
2

If you want to encrypt using custom IV, then you can encrypt like...

    let iv = CryptoJS.lib.WordArray.random(IV_LENGTH);
    const key = CryptoJS.enc.Utf8.parse(ENCRYPTION_KEY);

    let encrypted = CryptoJS.AES.encrypt(text, key, { iv })

    const encrypted = encrypted.iv+':'+encrypted.ciphertext
Sagar M
  • 972
  • 8
  • 13