无法使用 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 和 UUIDs 之间创建一个双射映射,使用一些填充,将 awids 作为初始输入。

在尝试使用 java.util.Base64 时,我没有在再次解码和编码后获得初始值。我犯了什么愚蠢的错误? :)

对于我在下面展示的可重现示例,输出是错误的,因为输入字符串在 decode()-encode() 之后没有返回并且双射没有保留(Q39s/LQ39s/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=7272/6=12.