首页 > 解决方案 > Cipher 类中的“NoPadding”参数究竟做了什么?

问题描述

Java 的Cipher类支持那里列出的转换。其中有几个NoPadding变种:

起初我假设这里的“填充”是指用于填充最后一个明文块的方法,如果明文的大小不是密码块大小的倍数。

但在这种情况下,如何将 ECB 或 CBC 之类的分组密码模式与“无填充”一起使用?假设我们AES/ECB/NoPadding用来加密一个 250 位的消息。明文的第一个块显然是消息的前 128 位。第二个明文块的最后 6 位是什么?

标签: javaencryptioncryptographypaddingblock-cipher

解决方案


嗯,首先,您不能Cipher以任何方式直接提供 250 位消息。原因是 - 就像在大多数运行时一样 - 字节是您可以处理的最小数据量。如果要对 250 位进行编码,则必须考虑以字节为单位对这些位进行编码(例如,通过指示最后一个字节中不使用的位,如 DER 编码 ASN.1 的值编码所执行的那样定义的位串)。


仅 11 位设置为 1 的 DER BIT STRING 示例:

05 FF E0

这里最后一个字节中有 5 个未使用的位(设置为 value 0)。所以第一个字节被简单地牺牲来表明这个事实——它不是值的一部分。


其次,即使输入8 位的倍数,那么您仍然无法使用NoPaddingECB 或 CBC 模式进行加密,除非输入本身已经是块大小的倍数。如果您指定NoPadding,则确实没有添加任何字节(甚至没有添加值字节),因此如果明文大小不是块大小的倍数(AES 为 16 个字节,DES 和 DES-EDE 为 8 个00字节),您将得到一个。IllegalBlockSizeException

明文大小是一起提供给updatedoFinal方法的输入字节数。最后一个块之前的块的字节将在需要的地方缓冲 - 只有最终计数很重要。


Cipher#doFinal文档中:

抛出:

    ...
    IllegalBlockSizeException- 如果此密码是块密码,则未请求填充(仅在加密模式下),并且此密码处理的数据的总输入长度不是块大小的倍数;或者如果此加密算法无法处理提供的输入数据。
    ...

请注意,在我看来,这是一个糟糕的描述:在解密期间,如果大小不是块大小的倍数,则doFinal 无论使用何种填充(同样,仅适用于 ECB 和 CBC 模式)都会引发此异常。毕竟,只有在分组密码和操作模式解密之后才会发生取消填充。

当然,ECB & CBC 模式在加密期间的输出应该始终是块大小的倍数,这意味着密文在解密之前被截断或以其他方式更改。


因为不需要 GCM 填充(其中使用了 CTR 模式加密),所以这NoPadding是唯一现实的选择,并且任何数量的数据都可以 - 但同样要加密的最小元素Cipher是一个字节。即使 GCM算法是以位而不是字节为单位指定的,此实现也不支持它。


推荐阅读