来自Java AES实现中的位移字节和整数的奇怪结果

时间:2014-02-23 06:34:13

标签: java bytearray aes bit-shift

我无法理解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,以及屏蔽和移位实际发生了什么。我查看了其他答案,但无法弄清楚为什么他们在这种情况下不工作。谢谢!

2 个答案:

答案 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中的填充位。