我无法理解Java如何通过逐位运算将字节提升为整数。我正在尝试实现AES,虽然我的输出作为2d字节数组是正确的,但我最终需要将它存储在1d int数组中。但是,以下代码会更改某些预期值
ciphertexts[0] = ((state[0][0] & 0xFF) << 24) ^ ((state[1][0] & 0xFF) << 16)
^ ((state[2][0] & 0xFF) << 8) ^ state[3][0];
ciphertexts[1] = ((state[0][1] & 0xFF) << 24) ^ ((state[1][1] & 0xFF) << 16)
^ ((state[2][1] & 0xFF) << 8) ^ state[3][1];
ciphertexts[2] = ((state[0][2] & 0xFF) << 24) ^ ((state[1][2] & 0xFF) << 16)
^ ((state[2][2] & 0xFF) << 8) ^ state[3][2];
ciphertexts[3] = ((state[0][3] & 0xFF) << 24) ^ ((state[1][3] & 0xFF) << 16)
^ ((state[2][3] & 0xFF) << 8) ^ state[3][3];
我没有特别期望使用0xFF屏蔽来提供帮助,因为掩码应该只返回原始字节值,但后来我尝试了这个:
int zero = ((state[0][0] & 0xFF) << 24);
int one = ((state[0][1] & 0xFF) << 16);
int two = ((state[0][2] & 0xFF) << 8) ;
int three = (state[0][3] & 0xFF);
int total = zero ^ one ^ two ^ three;
printhex(zero);
printhex(one);
printhex(two);
printhex(three);
printhex(total);
其中给出了以下输出:
69000000
006A0000
0000D800
00000070
696AD870
我正在尝试使用上面的代码。如果没有屏蔽,以下代码将提供以下输出:
int zero = (state[0][0] << 24);
int one = (state[0][1] << 16);
int two = (state[0][2] << 8);
int three = state[0][3];
int total = zero ^ one ^ two ^ three;
69000000
006A0000
FFFFD800
00000070
9695D870
我也尝试过在我看来更明智的东西,这是在移动后掩盖,同样搞砸了输出:
ciphertexts[0] = ((state[0][0] << 24) & 0xFFFFFFFF) ^
((state[1][0] << 16) & 0xFFFFFF) ^ ((state[2][0] << 8) & 0xFFFF)
^ state[3][0];
ciphertexts[1] = ((state[0][1] << 24) & 0xFFFFFFFF) ^
((state[1][1] << 16) & 0xFFFFFF) ^ ((state[2][1] << 8) & 0xFFFF)
^ state[3][1];
ciphertexts[2] = ((state[0][2] << 24) & 0xFFFFFFFF) ^
((state[1][2] << 16) & 0xFFFFFF) ^ ((state[2][2] << 8) & 0xFFFF)
^ state[3][2];
ciphertexts[3] = ((state[0][3] << 24) & 0xFFFFFFFF) ^
((state[1][3] << 16) & 0xFFFFFF) ^ ((state[2][3] << 8) & 0xFFFF)
^ state[3][3];
“乱搞”的意思是:
ciphertext at round 9 is 963b1fd86a7b04302732488070b4c55a
而不是:
69C4E0D86A7B0430D8CDB78070B4C55A
所以我的问题是如何将整齐或字节组合成一个int,以及屏蔽和移位实际发生了什么。我查看了其他答案,但无法弄清楚为什么他们在这种情况下不工作。谢谢!
答案 0 :(得分:1)
这是一种缺乏unsigned
的语言的残酷
(如果他/她使用signed char
,即签名字节,则可以在C中获得相同的结果
让我们忽略转变,只关注作业和&
示例值 0xfe 而不是0xd8
(问题将发生在0x80和0xff之间的每个值)
有问题,java:
byte a = 0xfe;
int i = a;
有问题,C:
signed char a = 0xfe;
int i = a;
发生了什么:一个字节可以保持-128到+127之间的值
0xfe映射到负数(2补码): -2
......所以,我在i中得到值-2,而我不是8位,而是32位长
根据2补码的规则,这给出了0xfffffffe
(http://en.wikipedia.org/wiki/Two%27s_complement)
那么,&amp;改变,因为屏蔽0xfe首先用0xff
不应该改变价值?
是的,但是:由于&
是一个“计算”,如+ -
...
该值首先扩展为32位
(因为更适合处理器的ALU)
C / Asm程序员更可能知道这一点,
但正如你所看到的,它在Java中也很重要。
(如果对于比32bit更小的变量赋值,则为nessecary,
它会在计算后再次缩短)
IE中。首先,-2 = 0xfe变为32位-2 = 0xfffffffe,
然后屏蔽再次产生0xfe(已经是32位)...
分配给i。
答案 1 :(得分:1)
state[0][2]
的值是一个字节0xD8
。这个最重要的位设置为1:二进制:1101 1000
。在应用班次操作<<
之前,byte
会转换为int
。 Java并不关心字节是无符号的,它被视为有符号字节。因此,字节的最高有效位一直填充到int的最高位。
简而言之:对于字节,您需要使用0xFF的掩码,因为这会掩盖已经转换的int中的填充位。