DES-CBC 初始化向量只影响第一个块?

DES-CBC Initialization Vector only affects the first block?

我正在尝试使用 CBC 模式进行 DES 加密,我遇到了一些令人费解的事情。我有一个有效负载(不是我生成的,但我知道它的密钥),伴随着一个 IV,以及一个盐,它意味着与 IV 进行异或运算。在我第一次尝试解密这个 payload 时,我发现明文的字节 3-7 是垃圾,但明文的其余部分看起来是正确的。最后我发现我没有把盐混合进去。 salt 的前 3 个字节恰好为 0,这解释了为什么明文的前 3 个字节符合我的预期,但我无法理解的是为什么不正确的 IV 对字节 8 到末尾没有影响有效载荷。我发现我可以将 IV 和盐都设置为任何值(包括全零),而且只有第一个块(8 个字节)受到影响。

因为这都是高度不敏感的东西(即我在本地网络上的一个非常过时的 Cisco 交换机上用一个单词的小写密码摆弄 SNMPv3,更不用说它甚至没有支持 AES),我很乐意与您分享以下内容:

#include <stdio.h>
#include <openssl/des.h>

int main()
{
  DES_key_schedule key;
  DES_cblock privKey = { 0xc8, 0x13, 0x1b, 0xf0, 0x41, 0xf8, 0xae, 0xef };
  DES_cblock iv = { 0xb5, 0xed, 0x65, 0x57, 0x7c, 0x99, 0x54, 0xe4 };
  DES_cblock salt = { 0x00, 0x00, 0x00, 0x10, 0x63, 0xc6, 0x01, 0x82 };

  const uint8_t cipher[] = {
    0xc7, 0xf5, 0x53, 0xe5, 0xb3, 0x8a, 0x19, 0x8b,
    0x03, 0xde, 0x49, 0xbb, 0x47, 0x38, 0x73, 0xb7,
    0x96, 0x24, 0xa3, 0xba, 0x3a, 0x28, 0x79, 0x16,
    0x8b, 0xe4, 0xbf, 0xb6, 0xfc, 0xc2, 0x86, 0xc7,
    0x8f, 0x1e, 0x0c, 0x87, 0x53, 0xbe, 0xfc, 0x0a,
    0xcd, 0x6a, 0x1a, 0xb3, 0xaa, 0x44, 0xcc, 0xb1,
    0xc0, 0x5e, 0xe0, 0x98, 0x33, 0x26, 0x4b, 0xc4,
    0x73, 0xa2, 0x93, 0x72, 0x86, 0x5e, 0xd0, 0xdf,
    0x4f, 0x7f, 0x53, 0x92, 0xb5, 0xd6, 0x54, 0x16,
    0x18, 0x55, 0xe6, 0xc7, 0xb0, 0x6f, 0x6b, 0xa7,
    0x53, 0x13, 0x4a, 0x66, 0xc8, 0x65, 0xbf, 0x18,
    0x2d, 0x00, 0x1c, 0xe5, 0x2e, 0xbc, 0xb2, 0x7f,
    0x76, 0x03, 0x46, 0xaf, 0xac, 0xf7, 0xb3, 0x47,
    0xbe, 0x09, 0xcc, 0x78, 0x9b, 0xf7, 0xae, 0x8f,
    0x1f, 0xb7, 0xbb, 0xe6, 0x4f, 0x3a, 0xad, 0xdc,
    0x5d, 0xde, 0xb4, 0x68, 0x4d, 0x5a, 0x68, 0x59,
    0xa3, 0xc5, 0x33, 0x88, 0xad, 0x67, 0xa7, 0x2c,
    0x7a, 0xe0, 0x45, 0x37, 0x41, 0x2d, 0x5d, 0xeb,
    0x59, 0x20, 0xd1, 0x3e, 0x5f, 0x8b, 0x12, 0xb0,
    0xcd, 0x1d, 0xd5, 0xf0, 0x1a, 0xe7, 0x64, 0x41,
    0x37, 0xc1, 0xd1, 0x0c, 0x23, 0xc9, 0x90, 0x32,
    0xf8, 0x21, 0xb9, 0xd6, 0x0e, 0x0e, 0x78, 0x2d,
    0xf0, 0x79, 0x8c, 0x2c, 0x44, 0x04, 0x10, 0x48,
    0xd7, 0xdb, 0x4c, 0xe5, 0xeb, 0x40, 0xb4, 0x4a,
    0xa9, 0xb5, 0xf7, 0xa6, 0xce, 0x06, 0x28, 0xf4,
    0xac, 0x99, 0xf8, 0x01, 0x88, 0xde, 0xb8, 0x75,
    0x11, 0xb6, 0x2c, 0x87, 0x22, 0x8e, 0xfe, 0x3e,
    0x0b, 0x7b, 0x15, 0xaa, 0xed, 0x1d, 0xaf, 0xfa,
    0x88, 0x96, 0xd2, 0x8f, 0x57, 0xf8, 0xcd, 0xf6,
    0x14, 0x7c, 0xbf, 0x69, 0x3d, 0x3e, 0x61, 0x2c,
    0xb8, 0x01, 0x5a, 0x8a, 0x6a, 0xb1, 0x58, 0x0f,
    0xa7, 0xd7, 0xc7, 0x5b, 0xe0, 0x0b, 0x3f, 0x05,
    0x88, 0x85, 0xbb, 0xea, 0x82, 0x3e, 0x6f, 0xf4,
    0xb7, 0x52, 0x4c, 0xc4, 0xea, 0x51, 0xfd, 0xb6,
    0xc4, 0x5b, 0x65, 0x2e, 0xac, 0x29, 0x3c, 0x19,
    0x40, 0x08, 0x5d, 0xa7, 0xad, 0xa8, 0x8c, 0x61,
    0x78, 0xaa, 0xc4, 0xa2, 0x38, 0x72, 0x45, 0x84,
    0x3d, 0x94, 0x8c, 0xa3, 0xba, 0x88, 0xf4, 0x79,
    0x0a, 0x87, 0xc6, 0xe9, 0xd3, 0x0e, 0xd0, 0xd0
  };

  uint8_t plain[sizeof(cipher)];
  int i;

  for (i = 0; i < sizeof(DES_cblock); i++) {
    iv[i] ^= salt[i];
  }

  DES_set_odd_parity(&privKey);
  DES_set_key_unchecked(&privKey, &key);
  DES_ncbc_encrypt(cipher, plain, sizeof(cipher), &key, &iv, DES_DECRYPT);

  for (i = 0; i < sizeof(plain); i++) {
    if (i) {
      putchar(i % 8 ? ' ' : '\n');
    }

    printf("0x%02x", plain[i]);
  }

  putchar('\n');
}

编译:

gcc -o main main.c -lcrypto

运行 ./main 产生以下输出:

0x30 0x82 0x01 0x34 0x04 0x0c 0x80 0x00
0x00 0x09 0x03 0x00 0x00 0x24 0x13 0x70
0xb2 0xc1 0x04 0x00 0xa2 0x82 0x01 0x20
0x02 0x04 0x75 0xd7 0x16 0x29 0x02 0x01
0x00 0x02 0x01 0x00 0x30 0x82 0x01 0x10
0x30 0x82 0x01 0x0c 0x06 0x08 0x2b 0x06
0x01 0x02 0x01 0x01 0x01 0x00 0x04 0x81
0xff 0x43 0x69 0x73 0x63 0x6f 0x20 0x49
0x6e 0x74 0x65 0x72 0x6e 0x65 0x74 0x77
0x6f 0x72 0x6b 0x20 0x4f 0x70 0x65 0x72
0x61 0x74 0x69 0x6e 0x67 0x20 0x53 0x79
0x73 0x74 0x65 0x6d 0x20 0x53 0x6f 0x66
0x74 0x77 0x61 0x72 0x65 0x20 0x0d 0x0a
0x49 0x4f 0x53 0x20 0x28 0x74 0x6d 0x29
0x20 0x43 0x32 0x39 0x34 0x30 0x20 0x53
0x6f 0x66 0x74 0x77 0x61 0x72 0x65 0x20
0x28 0x43 0x32 0x39 0x34 0x30 0x2d 0x49
0x36 0x4b 0x32 0x4c 0x32 0x51 0x34 0x2d
0x4d 0x29 0x2c 0x20 0x56 0x65 0x72 0x73
0x69 0x6f 0x6e 0x20 0x31 0x32 0x2e 0x31
0x28 0x32 0x32 0x29 0x45 0x41 0x31 0x33
0x2c 0x20 0x52 0x45 0x4c 0x45 0x41 0x53
0x45 0x20 0x53 0x4f 0x46 0x54 0x57 0x41
0x52 0x45 0x20 0x28 0x66 0x63 0x32 0x29
0x0d 0x0a 0x54 0x65 0x63 0x68 0x6e 0x69
0x63 0x61 0x6c 0x20 0x53 0x75 0x70 0x70
0x6f 0x72 0x74 0x3a 0x20 0x68 0x74 0x74
0x70 0x3a 0x2f 0x2f 0x77 0x77 0x77 0x2e
0x63 0x69 0x73 0x63 0x6f 0x2e 0x63 0x6f
0x6d 0x2f 0x74 0x65 0x63 0x68 0x73 0x75
0x70 0x70 0x6f 0x72 0x74 0x0d 0x0a 0x43
0x6f 0x70 0x79 0x72 0x69 0x67 0x68 0x74
0x20 0x28 0x63 0x29 0x20 0x31 0x39 0x38
0x36 0x2d 0x32 0x30 0x30 0x39 0x20 0x62
0x79 0x20 0x63 0x69 0x73 0x63 0x6f 0x20
0x53 0x79 0x73 0x74 0x65 0x6d 0x73 0x2c
0x20 0x49 0x6e 0x63 0x2e 0x0d 0x0a 0x43
0x6f 0x6d 0x70 0x69 0x6c 0x65 0x64 0x20
0x46 0x72 0x69 0x20 0x32 0x37 0x2d 0x46

如果我改为用全零填充 ivsalt,则第一行将更改为以下内容,而其余部分保持不变:

0x85 0x6f 0x64 0x73 0x1b 0x53 0xd5 0x66

所以显而易见的问题是,这是怎么回事?

谁能回答我也想问问 这种行为在任何地方都 observed/documented 吗? 如果它提供的好处如此之少,为什么还要将 CBC 与 DES 一起使用(嗯,是的,我知道没有人真的 鼓励 再使用 DES)?

答案非常简单,与DES无关,与CBC模式有关。感谢@Topaco 向我指出。

IV 确实在执行加密时将更改传播到所有块,并且具有随机化每个块的内容的效果。两个相同的明文块将产生截然不同的密文,因此它限制了直接通过密文泄漏的信息量。

另一方面,CBC 允许您解密任何块,而无需先解密它之前的块。这允许并行化解密。但是,它恰恰导致了问题中出现的行为。除第一个块外,不需要 IV 来解密任何块。