【问题标题】:Java: Pad a string with ASCII 0 (NULL) to a multiple of 16 bytesJava:用 ASCII 0(NULL)填充一个字符串到 16 个字节的倍数
【发布时间】:2017-06-12 10:57:05
【问题描述】:

在 java 中将空填充添加到字符串末尾的最简单方法是什么?这是加密过程中的一个前置步骤,因此生成的字符串必须是 16 字节的倍数。

第一次尝试只是在字符串 1 中添加一个包含 null 的字符串。但这不起作用,因为 null 变成了“null”这个词。

第二次尝试: 将字符串转换为字节,并以某种方式附加空字节,但我无法想出一种方法来实现这一点。

第三次尝试: 初始化一个已知大大小的字节数组,例如 128 减去我的字符串中的字节长度,然后将我的字符串转换为字节,将两个数组复制在一起。所以像:

String stringToEncrypt = "some data" ;
byte[] stringToEncryptBytes = stringToEncrypt.getBytes("UTF-8");
int stringToEncryptByteLength = stringToEncryptBytes.length;

int sizeOfNullArray = 128 - stringToEncryptByteLength;
byte[] byteBlockOfNulls = new byte[sizeOfNullArray] ;

byte[] finalBytes = new byte[stringToEncryptByteLength + sizeOfNullArray];
System.arraycopy(stringToEncryptBytes, 0, finalBytes, 0, stringToEncryptByteLength); 
System.arraycopy(byteBlockOfNulls, 0, finalBytes, stringToEncryptByteLength, sizeOfNullArray);

但是当我将字节数组转换回字符串时,它只是乱码。所以我一定做错了什么。

【问题讨论】:

  • 嗯,我用过 toString() 之前没有用。现在我使用 string finalString = new String(finalBytes, "UTF-8") 正确地将字节转换回字符串,我可以看到正确的字符串。但是,我仍然很好奇我的第三次尝试是否是将带有空值的字符串填充到特定大小的最佳/最简单方法。

标签: java encryption padding


【解决方案1】:

当您说“这是加密过程中的前一步”时,您是在说 填充 明文以加密以达到块边界,还是 密钥拉伸 形成适当长度的密钥? “空填充”在两者中都不理想,但在密钥派生中是​​非常危险的。

如果您的问题是前者,您可能想使用PKCS7Padding(在Java中也称为PKCS5Padding),其中works in the following manner:

  • 如果消息长度小于完整的块边界(即lengthBytes % 16 != 0),则最后一个块用 N 个字节填充,其中 N = 16 - (lengthBytes % 16) ,每个字节的值为N。例如,如果最后一个块为 13 个字节,则将使用 0x03 0x03 0x03 填充以达到 16 的长度。
  • 如果消息长度是块大小的整数倍(即lengthBytes % 16 == 0),则会添加一个完整的0x10 块(16 字节)。

上面的链接解释了这一点的全部原因。出于多种原因,不推荐使用零填充/空填充方案。

如果这实际上是关于如何实现练习代码的问题,则以下方法有效(在单个块上操作,而不是完整的明文):

String plaintext = "This is text"; // 12 bytes
byte[] plainBytes = plaintext.getBytes(StandardCharsets.UTF_8);
byte[] paddedBytes = new byte[16];
System.arraycopy(plainBytes, 0, paddedBytes, 0, plainBytes.length);

assert paddedBytes.length == 16;
assert paddedBytes[12] == (byte) 0x00;
assert paddedBytes[13] == (byte) 0x00;
assert paddedBytes[14] == (byte) 0x00;
assert paddedBytes[15] == (byte) 0x00;

如果转换回字符串,生成的字节数组将如下所示。

This is text    | <-- There will be four "unprintable character" indicators because 0x00 is not a printable character in UTF-8

【讨论】:

  • 将明文块填充为 16 的倍数,当明文块本身的大小可以在 33 字节到 100 字节左右之间变化时。这是与供应商的接口的一部分,所以我的双手被束缚了。我不能使用 PKCS5 或 7,因为供应商特别希望 ASCII 0(空)作为字符串末尾的填充。
  • 好的,在这种情况下,只需分配 byte[L] 其中 L 是您想要的消息长度和 16 的倍数,然后如上所述使用 System.arraycopy() 应该可以工作。需要明确的是 - block 不是“16 的倍数”(嗯,它正好是 1 * 16);每个块 是 16 个字节,填充的 消息 应该是 16 的倍数 (>= 1)。
  • 感谢您的信息。稍后在代码中我这样做: Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); .我假设这是我想要的密码实例,因为我正在手动处理填充。我会尽快与供应商交谈,看看他们是否可以使用更标准的 PKCS 东西。
  • 是的。另请注意,CBC 需要为每个消息加密提供唯一且不可预测的 IV,仅提供机密性(而不是完整性或真实性),并且不能并行加密。您还应该询问供应商是否支持像 GCM 这样的 AEAD 模式,或者在密文上添加 MAC。
猜你喜欢
  • 2011-02-01
  • 2011-07-23
  • 1970-01-01
  • 2021-11-22
  • 1970-01-01
  • 2014-11-19
  • 1970-01-01
  • 2022-01-12
  • 1970-01-01
相关资源
最近更新 更多