Openssl 使用 openssl 进行 DES/3DES 会产生奇怪的结果吗?

Openssl 使用 openssl 进行 DES/3DES 会产生奇怪的结果吗?

我正在努力实现一个包含 DES 和 3DES 功能的支持库。作为验证步骤,我用它来openssl检查我的结果。对于一个简单的测试,我做了以下操作:

echo -n "AAAAAAAA" | openssl enc -e -des-ecb -nosalt -pass pass:AABBCCDD | xxd
00000000: 2976 3faf 5e27 7187 5897 c640 c38c cf8b  )v?.^'q.X..@...

也就是说,ECB mod、明文AAAAAAAA、密钥AABBCCDD,并且不加盐。但这里有两件事让我感到奇怪。

(1)对于 64 位输入和 64 位块大小,我期望输出的长度与输入的长度相同

(2)如果我将输入加倍至 128 位(全部为 A,块长度为两倍),我实际上会得到 24 字节的密文

$ echo -n "AAAAAAAAAAAAAAAA" | openssl enc -e -des-ecb -nosalt -pass pass:AABBCCDD | xxd
00000000: 2976 3faf 5e27 7187 2976 3faf 5e27 7187  )v?.^'q.)v?.^'q.
00000010: 5897 c640 c38c cf8b                      X..@....

在进行最后一个实验时,我注意到密文确实在块大小边界上重复(参见2976 3faf 5e27 7187重复)。但是当达到此阈值时,我们会得到额外的 8 个字节密文。也就是说,使用 7 个 A 会得到 8 个字节密文,但使用 8 个 A 会得到 16 个字节密文。所以这个边界条件的处理似乎有些奇怪?

无论如何,我检查了一些随机的在线 DES 工具,没有一个与 openssl 提供的匹配 - 但它们都相互匹配。从这些工具中,我认为上面第一个示例的正确密文是

54 55 ab a4 a2 b0 83 38

或者它们全都错了,都是一样。这是怎么回事?

答案1

你要求openssl使用你的密钥作为密码,这是一个需要经过 KDF 函数处理才能用作所需长度的加密密钥的字符串。该-nosalt选项仅禁用 KDF 的盐输入,而不是禁用 KDF 的一般使用。

(OpenSSL 使用迭代 MD5 算法作为其默认 KDF,尽管最近的版本支持标准 PBKDF2,并且如果未启用通常会发出警告。)

要用作AABBCCDD文字 64 位密钥,必须通过以下方式指定-K

$ echo -n “AAAAAAAAAAAAAAAAAA”| openssl enc -e -des-ecb -K 4141424243434444 | hd
00000000  54 55 ab a4 a2 b0 83 38  54 55 ab a4 a2 b0 83 38 |图.....8图.....8|
00000010 55 f8 fc 5b 8f 9a 8e 8c |U..[....|
00000018

现在你有了两个密文块,加上一个填充。openssl enc工具适用PKCS#5 填充确保输入块的数量始终是完整的——例如,如果您的输入只有 15 个字节,那么最后一块将是 7 个字节,无法按原样加密。它不能简单地用零填充,因为接收者不知道可以安全地删除多少个零字节——毕竟,输入不一定是文本;它也可以是末尾带有一定数量合法零字节的二进制数据。

这里使用的填充的具体形式是使用最后一个字节来指示添加了多少填充,因此需要多少填充消除解密后。例如,如果最后一个明文块只有 6 个字节,则它将被填充0x02 0x02(在加密之前),以便接收者知道需要从解密块中删除 2 个字节。

但当你的明文的最后一块已经是全尺寸的,没有剩余空间来表明这一事实——你不能有零长度填充,因为接收者会仍然看看解密后的明文的最后一个字节,会感到困惑,因为最后一个字节是一些任意数据。因此,所有的添加填充块,由八个0x08字节组成。

在 ECB 模式下,您可以通过尝试将填充解密为数据来看到这一点:

$回显“55 f8 fc 5b 8f 9a 8e 8c  55 f8 fc 5b 8f 9a 8e 8c" | 取消十六进制 \
  | openssl enc -d -des-ecb -K 4141424243434444 \
  | 十六进制转储 -C
00000000  08 08 08 08 08 08 08 08                           |........|
00000008

对于较短的输入同样如此:

$ echo -n "AAAAAAAA啊啊啊啊“ \
  | openssl enc -e -des-ecb -K 4141424243434444 \
  | 十六进制转储 -C
00000000 54 55 ab a4 a2 b0 83 38  07 5b 23 e5 96 4b 2f 95  |图......8.[#..K/.|
00000010

$回显“07 5b 23 e5 96 4b 2f 95  55 f8 fc 5b 8f 9a 8e 8c" | 取消十六进制 \
  | openssl enc -d -des-ecb -K 4141424243434444 \
  | 十六进制转储 -C
00000000 41 41 41 41 4103 03 03                           |啊啊啊……|
00000008

如果你能保证输入始终是密码块大小的整数倍,那么你可以告诉 OpenSSL 使用以下方式禁用填充:-nopad选项并仅获取 16 个字节的密文:

$ echo -n "AAAAAAAAAAAAAAAAAA" \
  | openssl enc -e -des-ecb -nopad -K 4141424243434444 \
  | 十六进制转储 -C
00000000 54 55 ab a4 a2 b0 83 38 54 55 ab a4 a2 b0 83 38 |TU.....8TU.....8|
00000010

请注意,必须指定填充类型(或缺少填充)解密时,也。

还要注意,如果您使用加密-nopad但最后一个块未满,OpenSSL 将简单地将其丢弃并报告错误,因为它没有太多其他可做的事情。

相关内容