一、编码原理概述
1.1 为什么需要 Base 编码
二进制数据(如图片、文件、加密结果)无法直接在某些文本协议中传输(如 JSON、URL、邮件头)。Base 编码将二进制转为可打印 ASCII 字符,便于在文本环境中安全传输。
1.2 基本思路
- 将二进制按固定位数分组
- 每组映射到一个字符表中的一个字符
- 字符表通常为 64 或 32 个可打印字符,避免控制字符和易混淆字符
二、Base64 编码
2.1 原理
- 字符集:64 个字符(A-Z、a-z、0-9、+、/),每个字符表示 6 位(2^6 = 64)
- 分组规则:每 3 个字节(24 bit)分为 4 组,每组 6 bit,对应 4 个 Base64 字符
- 膨胀率:4/3 ≈ 1.33,即编码后数据约为原来的 4/3 倍
- 填充:若原始字节数不是 3 的倍数,用
=填充至 4 的倍数
2.2 编码示例(手算)
以 "Hi" 为例:
H = 0x48 = 01001000
i = 0x69 = 01101001
共 16 bit,补 0 至 24 bit
分组(每 6 bit):
010010 | 000110 | 100100 | 100000(补 0)
18 | 6 | 36 | 32
S | G | k | g
结果:SGk= (末尾一个 = 表示填充)
2.3 变体
| 变体 | 说明 | 字符集 |
|---|---|---|
| 标准 Base64 | RFC 4648 | A-Za-z0-9+/ |
| URL Base64 | 文件名/URL 安全 | 将 +、/ 替换为 -、_ |
| MIME Base64 | 邮件等 | 每 76 字符换行 |
三、Base32 编码
3.1 原理
- 字符集:32 个字符(A-Z、2-7),不含 0、1、8 等易与 O、I、B 混淆的字符
- 分组规则:每 5 个字节(40 bit)分为 8 组,每组 5 bit,对应 8 个 Base32 字符
- 膨胀率:8/5 = 1.6,编码后数据约为原来的 1.6 倍
- 大小写:不区分(RFC 4648),解码时大小写均可
3.2 与 Base64 对比
| 特性 | Base64 | Base32 |
|---|---|---|
| 字符数 | 64 | 32 |
| 每字符位数 | 6 bit | 5 bit |
| 膨胀率 | 4/3 ≈ 1.33 | 8/5 = 1.6 |
| 紧凑性 | 更好 | 较差 |
| 可读性/易错性 | 一般 | 更好(无易混淆字符) |
| 典型应用 | 邮件、HTTP、图片嵌入 | TOTP、密钥、手动输入 |
3.3 应用场景
- TOTP 双因素认证:如 Google Authenticator 使用的密钥格式
- 需要易读、不易出错:手动输入时避免 0/O、1/I 混淆
- Base32hex:RFC 4648 第 7 节变体,使用 0-9、A-V
四、Java 使用示例
4.1 Base64(Java 8+ 内置)
import java.util.Base64;
// 标准 Base64
String text = "Hello, Base64!";
String encoded = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8));
System.out.println(encoded); // SGVsbG8sIEJhc2U2NCE=
byte[] decoded = Base64.getDecoder().decode(encoded);
System.out.println(new String(decoded, StandardCharsets.UTF_8)); // Hello, Base64!
// 无填充(部分场景需要)
Base64.Encoder encoder = Base64.getEncoder().withoutPadding();
String noPad = encoder.encodeToString(text.getBytes(StandardCharsets.UTF_8));
// URL 安全编码(+ / 替换为 - _)
String urlSafe = Base64.getUrlEncoder().encodeToString("data?key=value".getBytes(StandardCharsets.UTF_8));
// MIME 编码(每 76 字符换行)
Base64.Encoder mimeEncoder = Base64.getMimeEncoder();
byte[] mimeEncoded = mimeEncoder.encode(largeData);
4.2 Base32(Apache Commons Codec)
Java 标准库未内置 Base32,可使用 Apache Commons Codec:
// Maven: org.apache.commons:commons-codec
import org.apache.commons.codec.binary.Base32;
Base32 base32 = new Base32();
// 编码
String input = "Hello";
byte[] encoded = base32.encode(input.getBytes(StandardCharsets.UTF_8));
System.out.println(new String(encoded)); // JBSWY3DP
// 解码
byte[] decoded = base32.decode(encoded);
System.out.println(new String(decoded, StandardCharsets.UTF_8)); // Hello
4.3 Base32 编码(纯 Java 实现示例)
// 简化版 Base32 编码(RFC 4648 字母表)
private static final String BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
public static String encodeBase32(byte[] data) {
StringBuilder sb = new StringBuilder();
int buffer = 0;
int bitsLeft = 0;
for (byte b : data) {
buffer = (buffer << 8) | (b & 0xFF);
bitsLeft += 8;
while (bitsLeft >= 5) {
sb.append(BASE32_ALPHABET.charAt((buffer >> (bitsLeft - 5)) & 31));
bitsLeft -= 5;
}
}
if (bitsLeft > 0) {
sb.append(BASE32_ALPHABET.charAt((buffer << (5 - bitsLeft)) & 31));
}
return sb.toString();
}