Search code examples
javascriptjavaaes

Translating aes-js CTR into Java


I have a given encryption in javascript using aes-js and need to decrypt it in Java. The result is not the expected text, but some gibberish. I suspect that I have a wrong understanding of the initialization vector and the counter. I tried multiple iv, such as only 0, only 1, 1 to 16, 5 to 20 and 5 to 15 then 0 to 4.

This is the code I try to translate into java. It works fine.

var aesjs = require('aes-js')


test('encrypts text', () => {
    // An example 128-bit key (16 bytes * 8 bits/byte = 128 bits)
    var key = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];

    // Convert text to bytes
    var text = 'Text may be any length you wish, no padding is required.';
    var textBytes = aesjs.utils.utf8.toBytes(text);

    // The counter is optional, and if omitted will begin at 1
    var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
    var encryptedBytes = aesCtr.encrypt(textBytes);

    // To print or store the binary data, you may convert it to hex
    var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes);
    console.log(encryptedHex);
    // "a338eda3874ed884b6199150d36f49988c90f5c47fe7792b0cf8c7f77eeffd87
    //  ea145b73e82aefcf2076f881c88879e4e25b1d7b24ba2788"

    // When ready to decrypt the hex string, convert it back to bytes
    var encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex);

    // The counter mode of operation maintains internal state, so to
    // decrypt a new instance must be instantiated.
    var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
    var decryptedBytes = aesCtr.decrypt(encryptedBytes);

    // Convert our bytes back into text
    var decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes);
    console.log(decryptedText);
    // "Text may be any length you wish, no padding is required."
    expect(decryptedText).toBe(text);
});

But my java code does not work

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class SimpleCrypt {

    //encryptedHex = "a338eda3874ed884b6199150d36f49988c90f5c47fe7792b0cf8c7f77eeffd87
    //                ea145b73e82aefcf2076f881c88879e4e25b1d7b24ba2788"
    //key =[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
    public String simpleDecrypt(byte[] key, String encryptedHex) throws NoSuchPaddingException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        // js: var encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex);
        byte[] encryptedBytes = DatatypeConverter.parseHexBinary(encryptedHex);

        // js: var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
        final Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
        byte[] iv = new byte[]{5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; //aes-js uses a Counter initialized with 5
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), ivSpec);

        // js: var decryptedBytes = aesCtr.decrypt(encryptedBytes);
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);

        // js var decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes);
        String decryptedText = new String(decryptedBytes);
        return decryptedText;
        //#�\t<�0��m�A�RE�ʶ\n� �p�')�+pG/I��5r1��t�R���j��e
    }
}

The encrypted byte arrays are the same (also java is signed, while js is unsigned) but the decrypted byte arrays are not. What would be the correct iv or am I doing something else wrong?


Solution

  • To give this thread an accepted answer: Topaco is right. The correct iv is byte[] iv = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5};