无法使用 java.util.Base64 正确编码和解码
fail to correctly encode and decode using java.util.Base64
设 "awids" 为 base 64 (A-Z a-z 0-9 "-" "@"
) 中的 12 个字符长度 ID。这是输入。
我的最终目标是在这些 awids 和 UUID
s 之间创建一个双射映射,使用一些填充,将 awids 作为初始输入。
在尝试使用 java.util.Base64
时,我没有在再次解码和编码后获得初始值。我犯了什么愚蠢的错误? :)
对于我在下面展示的可重现示例,输出是错误的,因为输入字符串在 decode()
-encode()
之后没有返回并且双射没有保留(Q39s/L
和Q39s/A
将两者映射到相同的值)。
------------------------------------------> Q39s/L (6 [51 33 39 73 2f 4c])
4 [43 7f 6c fc] -> 6 [51 33 39 73 2f 41] -> Q39s/A (6 [51 33 39 73 2f 41])
4 [43 7f 6c fc] -> 6 [51 33 39 73 2f 41] -> Q39s/A (6 [51 33 39 73 2f 41])
这是一个可重现的例子:
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.StringJoiner;
public class WhosebugQuestion {
public static void main(String[] args) {
String halfAwid = "Q39s/L";
byte[] sigBits = Base64.getDecoder().decode(halfAwid.getBytes(StandardCharsets.UTF_8));
byte[] actualSigBits = Base64.getEncoder().withoutPadding().encode(sigBits);
String actualHalfAwid = new String(actualSigBits, StandardCharsets.UTF_8);
byte[] sigBits2 = Base64.getDecoder().decode(halfAwid.getBytes(StandardCharsets.UTF_8));
byte[] actualSigBits2 = Base64.getEncoder().withoutPadding().encode(sigBits2);
String actualHalfAwid2 = new String(actualSigBits2, StandardCharsets.UTF_8);
System.out.println("----------------------------------------------> "
+ halfAwid + " (" + toHexString(halfAwid) + ") "
+ "\n"
+ " "
+ toHexString(sigBits) + " -> "
+ toHexString(actualSigBits) + " -> "
+ actualHalfAwid + " (" + toHexString(actualHalfAwid) + ") "
+ "\n"
+ " "
+ toHexString(sigBits2) + " -> "
+ toHexString(actualSigBits2) + " -> "
+ actualHalfAwid2 + " (" + toHexString(actualHalfAwid2) + ")"
+ "");
}
private static String toHexString(byte[] bytes) {
StringJoiner joiner = new StringJoiner(" ", "" + bytes.length + " [", "]");
for (byte b : bytes) {
joiner.add(String.format("%02x", b));
}
return joiner.toString();
}
private static String toHexString(String text) {
return toHexString(text.getBytes());
}
}
请毫不犹豫地指出我在代码中犯的任何其他错误,即使它们与问题没有直接关系。谢谢。
如果您将编码数据视为整个字节(或 ASCII 字符)序列,则 Base64 编码不是所有输入大小的双射映射。 Base64 将八位单位编码为六位单位(每个单位产生 64 种可能的组合),因此当您编码四个字节时,换句话说 4×8=32
位,您将得到 32/6=5⅓
单位输出,这意味着输出的第六个单元不会使用所有位。
换句话说,当您将由 64 个定义字符中的六个组成的任意字符串视为 Base64 编码时,您会将一个包含 64⁶ 组合的字符串投影到具有 256⁴ 组合的六个字节的“源”序列,这意味着数据丢失。
如果您选择可以投影到整数单位的输入大小,则可以使用 Base64 编码作为双射映射,例如显然,六个源字节可以编码为八个 Base64 编码字节。但它不适用于六个编码字节。有趣的是,它适用于您实际需要的大小,因为九个源字节将被编码为正好十二个编码字节:9×8=72
、72/6=12
.
设 "awids" 为 base 64 (A-Z a-z 0-9 "-" "@"
) 中的 12 个字符长度 ID。这是输入。
我的最终目标是在这些 awids 和 UUID
s 之间创建一个双射映射,使用一些填充,将 awids 作为初始输入。
在尝试使用 java.util.Base64
时,我没有在再次解码和编码后获得初始值。我犯了什么愚蠢的错误? :)
对于我在下面展示的可重现示例,输出是错误的,因为输入字符串在 decode()
-encode()
之后没有返回并且双射没有保留(Q39s/L
和Q39s/A
将两者映射到相同的值)。
------------------------------------------> Q39s/L (6 [51 33 39 73 2f 4c]) 4 [43 7f 6c fc] -> 6 [51 33 39 73 2f 41] -> Q39s/A (6 [51 33 39 73 2f 41]) 4 [43 7f 6c fc] -> 6 [51 33 39 73 2f 41] -> Q39s/A (6 [51 33 39 73 2f 41])
这是一个可重现的例子:
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.StringJoiner;
public class WhosebugQuestion {
public static void main(String[] args) {
String halfAwid = "Q39s/L";
byte[] sigBits = Base64.getDecoder().decode(halfAwid.getBytes(StandardCharsets.UTF_8));
byte[] actualSigBits = Base64.getEncoder().withoutPadding().encode(sigBits);
String actualHalfAwid = new String(actualSigBits, StandardCharsets.UTF_8);
byte[] sigBits2 = Base64.getDecoder().decode(halfAwid.getBytes(StandardCharsets.UTF_8));
byte[] actualSigBits2 = Base64.getEncoder().withoutPadding().encode(sigBits2);
String actualHalfAwid2 = new String(actualSigBits2, StandardCharsets.UTF_8);
System.out.println("----------------------------------------------> "
+ halfAwid + " (" + toHexString(halfAwid) + ") "
+ "\n"
+ " "
+ toHexString(sigBits) + " -> "
+ toHexString(actualSigBits) + " -> "
+ actualHalfAwid + " (" + toHexString(actualHalfAwid) + ") "
+ "\n"
+ " "
+ toHexString(sigBits2) + " -> "
+ toHexString(actualSigBits2) + " -> "
+ actualHalfAwid2 + " (" + toHexString(actualHalfAwid2) + ")"
+ "");
}
private static String toHexString(byte[] bytes) {
StringJoiner joiner = new StringJoiner(" ", "" + bytes.length + " [", "]");
for (byte b : bytes) {
joiner.add(String.format("%02x", b));
}
return joiner.toString();
}
private static String toHexString(String text) {
return toHexString(text.getBytes());
}
}
请毫不犹豫地指出我在代码中犯的任何其他错误,即使它们与问题没有直接关系。谢谢。
如果您将编码数据视为整个字节(或 ASCII 字符)序列,则 Base64 编码不是所有输入大小的双射映射。 Base64 将八位单位编码为六位单位(每个单位产生 64 种可能的组合),因此当您编码四个字节时,换句话说 4×8=32
位,您将得到 32/6=5⅓
单位输出,这意味着输出的第六个单元不会使用所有位。
换句话说,当您将由 64 个定义字符中的六个组成的任意字符串视为 Base64 编码时,您会将一个包含 64⁶ 组合的字符串投影到具有 256⁴ 组合的六个字节的“源”序列,这意味着数据丢失。
如果您选择可以投影到整数单位的输入大小,则可以使用 Base64 编码作为双射映射,例如显然,六个源字节可以编码为八个 Base64 编码字节。但它不适用于六个编码字节。有趣的是,它适用于您实际需要的大小,因为九个源字节将被编码为正好十二个编码字节:9×8=72
、72/6=12
.