Base32 编码和解码

什么是 Base32 编码?

Base32 是一种数据编码机制,使用 32 个可打印字符(字母 A-Z 和数字 2-7)对任意字节数据进行编码的方案,编码后的字符串不用区分大小写并排除了容易混淆的字符,可以方便地由人类使用并由计算机处理。

它主要用来把二进制数据编码成可见的字符串。它的编码规则是:任意给定一个二进制数据,以 5 个位(bit))为一组进行切分(注意:base64 以 6 个位 (bit) 为一组),对切分而成的每个组进行编码得到 1 个可见字符。Base32 编码表字符集中的字符总数为 2 的 5 次方 32 个,这也是 Base32 名字的由来。

Commons Codec 实现

在 Commons Codec 中,通过 Base32、Base32InputStream 和 Base32OutputStream 类实现 base32 编码和解码。

Base32

提供 RFC 4648 定义的 Base32 编码和解码。可以使用各种构造函数以以下方式对类进行参数化:

  • 是否使用 “base32hex” 变体而不是默认的 “base32”

  • 行长:默认值76。在编码后的数据中,不是8的倍数的行长度本质上仍然是8的倍数。

  • 行分隔符:默认为CRLF(“\r\n”)

注意:此类直接对字节流进行操作,而不对字符流进行操作;此类也是线程安全的;

构造方法说明

Base32(
    int lineLength,
    byte[] lineSeparator,
    boolean useHex,
    byte padding,
    CodecPolicy decodingPolicy)

创建用于解码和编码的 Base32/Base32 十六进制编解码器。编码时,在构造函数中给出了行长和行分隔符。在编码数据中,不是8的倍数的行长实际上仍将是8的倍数。参数说明:

  • lineLength:每行编码数据最多为给定长度(四舍五入为最接近的8的倍数)。如果 lineLength <= 0,则输出将不会分为几行。解码时忽略。

  • lineSeparator:每一行编码数据将以此字节序列结尾

  • useHex:如果为true,则使用 Base32 十六进制字母,否则使用 Base32 字母

  • padding:用作填充字节的字节

  • decodingPolicy:解码策略

实例

(1)使用默认构造方法构造 Base32 对象,代码如下:

Base32 base32 = new Base32();
// 加密
byte[] bytes = base32.encode("hello world".getBytes());
System.out.println(new String(bytes));
// 解密
System.out.println(new String(base32.decode(bytes)));

(2)演示指定自定的 padding,使用指定的 padding 去进行填充,代码如下:

// 指定使用 “#” 进行填充
Base32 base32 = new Base32((byte)'#');
// 加密
byte[] bytes = base32.encode("hello world".getBytes());
System.out.println(new String(bytes));
// 解密
System.out.println(new String(base32.decode(bytes)));

输出结果:

NBSWY3DPEB3W64TMMQ######
hello world

(3)演示 useHex 的用法

在正式演示 useHex 的用法之前,我们先看看 Base32 中对 base32 编码字符表映射。源码定义如下:

/**
 * 该数组是一个查找表,该表将5位正整数索引值转换为RFC 4648表3中指定的 “Base32 Alphabet” 等效项。
 */
private static final byte[] ENCODE_TABLE = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    '2', '3', '4', '5', '6', '7',
};

/**
 * 此数组是一个查找表,该表将5位正整数索引值转换为RFC 4648表4中指定的 “Base32 Hex Alphabet” 等效项。
 */
private static final byte[] HEX_ENCODE_TABLE = {
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
};

实例:分别演示使用和不使用 useHex 选项,代码如下:

String str = "hello world";
// 加密(Base32 Alphabet)
Base32 base32 = new Base32(false);
byte[] bytes = base32.encode(str.getBytes());
System.out.println(new String(bytes));
// 加密(Base32 Hex Alphabet)
base32 = new Base32(true);
bytes = base32.encode(str.getBytes());
System.out.println(new String(bytes));

输出结果:

NBSWY3DPEB3W64TMMQ======
D1IMOR3F41RMUSJCCG======

(4)演示行长度的使用,代码如下:

// 指定编码后每行最多显示 16 个字符
Base32 base32 = new Base32(16);
// 加密
byte[] bytes = base32.encode("hi, welcome administrator".getBytes());
System.out.println(new String(bytes));
// 解密
System.out.println(new String(base32.decode(bytes)));

输出结果:

NBUSYIDXMVWGG33N
MUQGCZDNNFXGS43U
OJQXI33S

(5)演示自定义行分隔符,代码如下:

Base32 base32 = new Base32(16, "%%".getBytes());
// 加密
byte[] bytes = base32.encode("hi, welcome administrator".getBytes());
System.out.println(new String(bytes));
// 解密
System.out.println(new String(base32.decode(bytes)));

输出结果:

NBUSYIDXMVWGG33N%%MUQGCZDNNFXGS43U%%OJQXI33S%%
hi, welcome administrator

(6)演示 decodingPolicy 解码策略

在演示 Base32 类的解码策略之前,我们先看看策略 CodecPolicy。CodecPolicy 类定义了编码和解码策略。该类是一个枚举类,提供如下两个枚举:

  • LENIENT:宽松策略

  • STRICT:严格策略

实例:演示使用严格策略 CodecPolicy.STRICT,代码如下:

/**
 * 1.指定每行长度 16 字节
 * 2.使用 \r\n 作为换行符
 * 3.不启用 useHex
 * 4.填充字符为 #
 * 5.解密策略为 CodecPolicy.STRICT
 */
Base32 base32 = new Base32(16, "\r\n".getBytes(),
    false, (byte)'#', CodecPolicy.STRICT);
// 加密
byte[] bytes = base32.encode("Process finished with exit code".getBytes());
System.out.println(new String(bytes));
// 解密
System.out.println(new String(base32.decode(bytes)));

输出结果:

KBZG6Y3FONZSAZTJ
NZUXG2DFMQQHO2LU
NAQGK6DJOQQGG33E
MU######

Base32InputStream

以流方式(大小不受限制)提供Base32编码和解码。编码时,默认的 lineLength 为 76 个字符,默认的 lineEnding 为CRLF,但是可以使用适当的构造函数来覆盖这些字符。

Base32InputStream 的默认行为是 DECODE,而 Base32OutputStream 的默认行为是ENCODE,但是可以使用其他构造函数来覆盖此行为。

由于此类直接在字节流而不是字符流上运行,因此它被硬编码为仅编码/解码与较低的 127 个 ASCII 图表兼容的字符编码(ISO-8859-1,Windows-1252,UTF-8, 等等)。

当输入字节包含有效编码无法创建的剩余尾随位时,可以设置解码行为。 这些可以是最后一个字符或整个字符中未使用的位。 默认模式是宽容解码。

  • 宽松:任何可能的尾随位都组成8位字节。 其余的将被丢弃。

  • 严格:如果尾随位不是有效编码的一部分,则解码将引发IllegalArgumentException。 最后一个字符中任何未使用的位必须为零。 不允许对整个最终字符进行计数。

启用严格解码后,预计解码后的字节将被重新编码为与原始字节匹配的字节数组,即最终字符不会发生变化。 这要求输入字节使用与编码器相同的填充和字母。

实例:

byte[] bytes = "NBSWY3DPEB3W64TMMQ======".getBytes();
ByteArrayInputStream input = new ByteArrayInputStream(bytes);
Base32InputStream base32InputStream = new Base32InputStream(input);

// 直接从输入流读取,读取出来的就是明文了
byte[] buffer = new byte[1024];
int len = base32InputStream.read(buffer);
System.out.println(new String(buffer, 0, len));

Base32OutputStream

以流方式(大小不受限制)提供Base32编码和解码。编码时,默认的 lineLength 为 76 个字符,默认的 lineEnding 为 CRLF,但是可以使用适当的构造函数来覆盖这些字符。

Base32OutputStream 的默认行为是 ENCODE,而 Base32InputStream 的默认行为是 DECODE。但是可以通过使用其他构造函数来覆盖此行为。

由于此类直接在字节流而不是字符流上运行,因此它被硬编码为仅编码/解码与较低的127个ASCII图表兼容的字符编码(ISO-8859-1,Windows-1252,UTF-8,等等)。

注意:必须在写入最后一个字节后关闭流,否则最后的填充将被忽略,并且结果数据将不完整/不一致。

当输入字节包含有效编码无法创建的剩余尾随位时,可以设置解码行为。这些可以是最后一个字符或整个字符中未使用的位。默认模式是宽容解码。

  • 宽松:任何可能的尾随位都组成8位字节。其余的将被丢弃。

  • 严格:如果尾随位不是有效编码的一部分,则解码将引发 IllegalArgumentException。最后一个字符中任何未使用的位必须为零。不允许对整个最终字符进行计数。

启用严格解码后,预计解码后的字节将被重新编码为与原始字节匹配的字节数组,即最终字符不会发生变化。这要求输入字节使用与编码器相同的填充和字母。

实例:

(1)Base32OutputStream 默认为编码,我们通过 write 方法写入数据时,会自动进行编码。代码如下:

ByteArrayOutputStream output = new ByteArrayOutputStream();
Base32OutputStream base32OutputStream = new Base32OutputStream(output);
base32OutputStream.write("hello world".getBytes());
base32OutputStream.close();
// 输出编码字符串
System.out.println(new String(output.toByteArray()));

(2)通过构造函数指定 Base32OutputStream 行为为解码,代码如下:

ByteArrayOutputStream output = new ByteArrayOutputStream();
Base32OutputStream base32OutputStream = new Base32OutputStream(output, false);
base32OutputStream.write("NBSWY3DPEB3W64TMMQ======".getBytes());
base32OutputStream.close();
// 输出编码字符串
System.out.println(new String(output.toByteArray()));
说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号