Base64 与 Base32 编码解析

2 阅读3分钟

一、编码原理概述

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 变体

变体说明字符集
标准 Base64RFC 4648A-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 对比

特性Base64Base32
字符数6432
每字符位数6 bit5 bit
膨胀率4/3 ≈ 1.338/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();
}