Search code examples
javascriptjavaencryptionaes

Encrypted bytes is not recognized by Java from Javascript?


I've done the AES CTR implementation on Javascript. Now I'm intended to share the encrypted bytes to Java for further processing. However I'm unable to assign the value return from JavaScript's Uint8Array to Java's byte[].

Below's a sample data in Uint8Array format (After AES CTR encryption)

[71, 193, 223, 190, 6, 104, 11, 235, 249, 96, 54, 192, 233, 41, 198, 188, 15, 218, 10, 0, 61, 95, 58, 122, 74, 169, 27, 228, 121, 224, 128, 124, 198, 183, 23, 36, 89, 105, 184, 59, 245, 115, 244, 22, 122, 207, 217, 219, 160, 2, 227, 175, 134, 66, 165, 73, 102, 52, 14, 150, 182, 187, 228, 173, 96, 68, 11, 35, 166, 247, 45, 18, 202, 99, 81, 185, 216, 240, 66, 10, 105, 122, 45, 83]

When I pass try hardcoding value received from JavaScript in Java under byte[] as show, I received following complain.

byte[] resp = {71, 193, 223, 190, 6, 104, 11, 235, 249, 96, 54, 192, 233, 41, 198, 188, 15, 218, 10, 0, 61, 95, 58, 122, 74, 169, 27, 228, 121, 224, 128, 124, 198, 183, 23, 36, 89, 105, 184, 59, 245, 115, 244, 22, 122, 207, 217, 219, 160, 2, 227, 175, 134, 66, 165, 73, 102, 52, 14, 150, 182, 187, 228, 173, 96, 68, 11, 35, 166, 247, 45, 18, 202, 99, 81, 185, 216, 240, 66, 10, 105, 122, 45, 83};

// The complain is as follow:
Required type: byte
Provided: int

I did this for resolving to the above issue:

byte[] resp = {71, (byte)193, (byte)223, (byte)190, 6, 104, 11, (byte)235, (byte)249, 96, 54, (byte)192, (byte)233, 41, (byte)198, (byte)188, 15, (byte)218, 10, 0, 61, 95, 58, 122, 74, (byte)169, 27, (byte)228, 121, (byte)224, (byte)128, 124, (byte)198, (byte)183, 23, 36, 89, 105, (byte)184, 59, (byte)245, 115, (byte)244, 22, 122, (byte)207, (byte)217, (byte)219, (byte)160, 2, (byte)227, (byte)175, (byte)134, 66, (byte)165, 73, 102, 52, 14, (byte)150, (byte)182, (byte)187, (byte)228, (byte)173, 96, 68, 11, 35, (byte)166, (byte)247, 45, 18, (byte)202, 99, 81, (byte)185, (byte)216, (byte)240, 66, 10, 105, 122, 45, 83};

Below is to show some implemented of the code

JS

    var key = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    var textBytes = aesjs.utils.utf8.toBytes("textToEncrypt");
    var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(16));
    var encryptedBytes = aesCtr.encrypt(textBytes);

# Outcome of encryptedBytes
[71, 193, 223, 190, 6, 104, 11, 235, 249, 96, 54, 192, 233, 41, 198, 188, 15, 218, 10, 0, 61, 95, 58, 122, 74, 169, 27, 228, 121, 224, 128, 124, 198, 183, 23, 36, 89, 105, 184, 59, 245, 115, 244, 22, 122, 207, 217, 219, 160, 2, 227, 175, 134, 66, 165, 73, 102, 52, 14, 150, 182, 187, 228, 173, 96, 68, 11, 35, 166, 247, 45, 18, 202, 99, 81, 185, 216, 240, 66, 10, 105, 122, 45, 83]

Java

byte[] keyBytes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
byte[] ivBytes = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};

# Here I assign the value I've received from `Javascript` into `resp`

byte[] resp = {71, (byte)193, (byte)223, (byte)190, 6, 104, 11, (byte)235, (byte)249, 96, 54, (byte)192, (byte)233, 41, (byte)198, (byte)188, 15, (byte)218, 10, 0, 61, 95, 58, 122, 74, (byte)169, 27, (byte)228, 121, (byte)224, (byte)128, 124, (byte)198, (byte)183, 23, 36, 89, 105, (byte)184, 59, (byte)245, 115, (byte)244, 22, 122, (byte)207, (byte)217, (byte)219, (byte)160, 2, (byte)227, (byte)175, (byte)134, 66, (byte)165, 73, 102, 52, 14, (byte)150, (byte)182, (byte)187, (byte)228, (byte)173, 96, 68, 11, 35, (byte)166, (byte)247, 45, 18, (byte)202, 99, 81, (byte)185, (byte)216, (byte)240, 66, 10, 105, 122, 45, 83};

cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] plaintext = cipher.doFinal(decoded);
String plaintextString = new String(plaintext, StandardCharsets.UTF_8); 

# Here I expect the value `textToEncrypt` to be decrypted under variable `plaintextString`.

Am I doing something wrong? Eg, the way Im setting the counter(IV) in Java.

FYI info

  • cipher.getAlgorithm() is SunJCE version 15

Solution

  • In the Java code the IV is specified incorrectly.

    The CTR Mode increments the passed IV from block to block. new aesjs.Counter(16) in the JavaScript code sets the starting value of the IV to 16.

    Therefore, the counterpart in the Java code is:

    byte[] ivBytes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16};
    

    With this change, decryption with the Java code works.


    Note that the security of CTR breaks down if key/IV pairs are used repeatedly. Since usually the key is fixed, this means that no fixed IV may be used (which however is the case in the posted code), see here for more details.

    Also, CTR does not provide any authentication of the message. This can be corrected with a MAC, alternatively the GCM mode can be used. This is based on the CTR mode and implicitly applies a MAC.


    For an explanation of the necessary adaptation of the binary data regarding the transfer from JavaScript to Java code, see the other answer.

    An alternative to the transfer of binary data would be to convert the data into a string using a binary to text encoding and to transfer this string. Typically Base64 or hex encoding is applied for this, e.g. with Base64:

    var key = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    var textBytes = aesjs.utils.utf8.toBytes("The quick brown fox jumps over the lazy dog");
    var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(16));
    var encryptedBytes = aesCtr.encrypt(textBytes);
    
    document.getElementById("bin").innerHTML = encryptedBytes; // 12,218,38,59,177,203,183,97,62,47,34,81,230,30,130,88,98,127,198,220,167,147,249,59,26,253,111,11,142,145,186,233,212,59,4,153,120,222,196,212,28,222,190
    document.getElementById("b64").innerHTML = ui8ToB64(encryptedBytes); // DNomO7HLt2E+LyJR5h6CWGJ/xtynk/k7Gv1vC46RuunUOwSZeN7E1Bzevg==    
    
    // from https://stackoverflow.com/a/11562550/9014097
    function ui8ToB64( arr ) {
        return btoa(String.fromCharCode.apply(null, arr));
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/aes-js/3.1.2/index.min.js"></script>
    <p style="font-family:'Courier New', monospace;" id="bin"></p>
    <p style="font-family:'Courier New', monospace;" id="b64"></p>

    On the Java side, the built-in Base64.Decoder#decode() can be used for the Base64 decoding.