简体   繁体   中英

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

The problem is due to the fact in java the byte data type is an 8-bit signed two's complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive) , as stated for example in the official datatypes tutorial. This implies that values like 193 are int and not byte , so the compiler complains about to find int values inside a byte array and you are obliged to cast them to byte with unpredictable results. You can consider to instead declare an int array or a short array instead of a byte array to solve your problem

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, eg 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM