java - Cipher 类中的“NoPadding”参数究竟做了什么?
问题描述
Java 的Cipher
类支持那里列出的转换。其中有几个NoPadding
变种:
- AES/CBC/NoPadding (128)
- AES/ECB/无填充 (128)
- AES/GCM/无填充 (128)
- DES/CBC/无填充 (56)
- DES/ECB/无填充 (56)
- DESede/CBC/NoPadding (168)
- DESede/ECB/无填充 (168)
起初我假设这里的“填充”是指用于填充最后一个明文块的方法,如果明文的大小不是密码块大小的倍数。
但在这种情况下,如何将 ECB 或 CBC 之类的分组密码模式与“无填充”一起使用?假设我们AES/ECB/NoPadding
用来加密一个 250 位的消息。明文的第一个块显然是消息的前 128 位。第二个明文块的最后 6 位是什么?
解决方案
嗯,首先,您不能Cipher
以任何方式直接提供 250 位消息。原因是 - 就像在大多数运行时一样 - 字节是您可以处理的最小数据量。如果要对 250 位进行编码,则必须考虑以字节为单位对这些位进行编码(例如,通过指示最后一个字节中不使用的位,如 DER 编码 ASN.1 的值编码所执行的那样定义的位串)。
仅 11 位设置为 1 的 DER BIT STRING 示例:
05 FF E0
这里最后一个字节中有 5 个未使用的位(设置为 value 0
)。所以第一个字节被简单地牺牲来表明这个事实——它不是值的一部分。
其次,即使输入是8 位的倍数,那么您仍然无法使用NoPadding
ECB 或 CBC 模式进行加密,除非输入本身已经是块大小的倍数。如果您指定NoPadding
,则确实没有添加任何字节(甚至没有添加值字节),因此如果明文大小不是块大小的倍数(AES 为 16 个字节,DES 和 DES-EDE 为 8 个00
字节),您将得到一个。IllegalBlockSizeException
明文大小是一起提供给update
和doFinal
方法的输入字节数。最后一个块之前的块的字节将在需要的地方缓冲 - 只有最终计数很重要。
从Cipher#doFinal
文档中:
抛出:
...
IllegalBlockSizeException
- 如果此密码是块密码,则未请求填充(仅在加密模式下),并且此密码处理的数据的总输入长度不是块大小的倍数;或者如果此加密算法无法处理提供的输入数据。
...
请注意,在我看来,这是一个糟糕的描述:在解密期间,如果大小不是块大小的倍数,则doFinal
无论使用何种填充(同样,仅适用于 ECB 和 CBC 模式)都会引发此异常。毕竟,只有在分组密码和操作模式解密之后才会发生取消填充。
当然,ECB & CBC 模式在加密期间的输出应该始终是块大小的倍数,这意味着密文在解密之前被截断或以其他方式更改。
因为不需要 GCM 填充(其中使用了 CTR 模式加密),所以这NoPadding
是唯一现实的选择,并且任何数量的数据都可以 - 但同样要加密的最小元素Cipher
是一个字节。即使 GCM算法是以位而不是字节为单位指定的,此实现也不支持它。
推荐阅读
- javascript - 由于标签“文件:”,脚本无法加载
- kotlin - 我什么时候应该让我的正常功能暂停功能?
- python-3.x - TypeError:输入类型不支持 ufunc 'isnan'
- group-by - 扩展列表以包括 SAS 中的因子变换
- r - flexboard 闪亮的桌子水平和垂直滚动条不起作用
- check-constraints - SQL Server:检查约束表达式
- firebase - 使用 StreamBuilder 从 Firebase(实时数据库)Flutter 获取数据
- java - 如何使用 Talend Open Studio 或在 java 中冻结 .xslx 的标头
- testing - 浏览器堆栈上的文本日志中的 Testcafe “章节”
- apache-flink - 如何在 Apache Flink 作业中获取 System.getProperties()