【问题标题】:Encrypted bytes is not recognized by Java from Javascript?Java从Javascript无法识别加密字节?
【发布时间】:2021-12-17 05:35:37
【问题描述】:

我已经在 J​​avascript 上完成了 AES CTR 实现。现在我打算将加密的字节共享给 Java 以进行进一步处理。但是我无法将返回值从JavaScript's Uint8Array 分配给Java's byte[]

下面是Uint8Array格式的示例数据(经过AES CTR加密)

[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 中的JavaScript 接收到的硬编码值时,byte[] 下显示,我收到以下投诉。

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

我这样做是为了解决上述问题

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};

下面是一些实现的代码

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`.

我做错了吗?例如,我在Java 中设置计数器(IV)的方式。

仅供参考

  • cipher.getAlgorithm() 是 SunJCE 版本 15

【问题讨论】:

  • 在生成的Uint8Array 周围尝试new Int8Array()。让我想起了JavaScript equivalent of Java's String.getBytes(StandardCharsets.UTF_8)
  • CTR 模式不使用填充,即密文应与明文具有相同的长度,因此对于textToEncrypt 13 个字节。你的密文比较长,所以数据不一致(可能textToEncrypt是个假人,那么请分享属于贴出来的密文的明文)。
  • 另外,Java代码中应用的IV不正确。在Java代码中使用的new aesjs.Counter(16)对应的IV是{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16}。如果我应用这个IV,我可以用Java代码成功地从JavaScript中解密密文。
  • @SebastianSimon,感谢分享链接。当我尝试在 Javascript 中做同样的事情时,这将对我有所帮助。要在 Java 中处理这个问题,你会建议我在byte[] 中处理它们吗?您可能已经注意到,从 Javascript 生成的一些值被视为Int。我不能直接接受这个参数作为byte[],Java会自己改变值。
  • @Topaco 眼光敏锐。是的,我提供了一个虚拟数据作为样本。先生,向您鞠躬 90 度,当您指向 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16} 时,您已更正了我的 IV 计数器。我不太确定如何早点做到这一点。如果您可以将此作为答案发布,我愿意接受:) 谢谢!

标签: javascript java encryption aes


【解决方案1】:

在 Java 代码中,IV 指定不正确。

CTR Mode 将传递的 IV 从一个块增加到另一个块。 JavaScript 代码中的new aesjs.Counter(16) 将IV 的起始值设置为16。

因此,Java代码中对应的是:

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

通过此更改,可以使用 Java 代码进行解密。


请注意,如果重复使用密钥/IV 对,CTR 的安全性就会崩溃。由于通常密钥是固定的,这意味着不能使用固定的 IV(但在发布的代码中就是这种情况),请参阅 here 了解更多详细信息。

此外,CTR 不提供消息的任何身份验证。这可以通过 MAC 进行纠正,或者可以使用GCM 模式。这是基于 CTR 模式并隐式应用 MAC。


有关从 JavaScript 到 Java 代码传输的二进制数据的必要调整说明,请参阅other answer

传输二进制数据的另一种方法是使用binary to text encoding 将数据转换为字符串并传输此字符串。通常为此应用 Base64 或十六进制编码,例如使用 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>

在Java端,可以使用内置的Base64.Decoder#decode()进行Base64解码。

【讨论】:

    【解决方案2】:

    问题是由于在java中字节数据类型是一个8位有符号二进制补码整数。它的最小值为 -128,最大值为 127(含),例如官方 datatypes 教程中所述。这意味着像 193 这样的值是 int 而不是 byte,因此编译器抱怨在字节数组中找到 int 值,您必须将它们强制转换为字节,结果无法预测。您可以考虑改为声明 int 数组或 short 数组或 char 数组而不是字节数组来解决您的问题

    【讨论】:

    • 感谢您指出此信息 :)
    猜你喜欢
    • 2018-07-02
    • 1970-01-01
    • 1970-01-01
    • 2016-09-23
    • 1970-01-01
    • 1970-01-01
    • 2013-02-17
    • 1970-01-01
    • 2014-05-25
    相关资源
    最近更新 更多