欢迎来到徐庆高(Tea)的个人博客网站
磨难很爱我,一度将我连根拔起。从惊慌失措到心力交瘁,我孤身一人,但并不孤独无依。依赖那些依赖我的人,信任那些信任我的人,帮助那些给予我帮助的人。如果我愿意,可以分裂成无数面镜子,让他们看见我,就像看见自己。察言观色和模仿学习是我的领域。像每个深受创伤的人那样,最终,我学会了随遇而安。
当前位置: 日志文章 > 详细内容

Java实现BASE64加解密算法的示例代码

2025年07月17日 Java
1. 项目背景详细介绍在网络通信、数据存储与传输以及各种协议交互中,二进制数据常常需要以文本形式表现,以便在 http、smtp、json 等纯文本协议中安全传递。base64 编码便是一种常用的方法

1. 项目背景详细介绍

在网络通信、数据存储与传输以及各种协议交互中,二进制数据常常需要以文本形式表现,以便在 http、smtp、json 等纯文本协议中安全传递。base64 编码便是一种常用的方法,它将任意二进制数据编码为可打印的 ascii 字符,保证在文本环境下不被破坏。

java 标准库中已有 java.util.base64 实现,但手写一套基于查表法(table-driven)的 base64 编解码算法,可以帮助我们:

  • 深入理解 base64 的编码原理与字符映射规则;
  • 掌握位运算与字节处理技巧;
  • 在不依赖库的环境中灵活集成到自定义框架或受限平台(如 android 早期版本)中。

本项目将使用 java 语言,从头实现基于查表法的 base64 编码与解码工具,支持标准 base64(含 +/= 填充)及 url 安全变体(-_、无填充)。

2. 项目需求详细介绍

功能需求

base64 编码

  • 输入任意 byte[],返回标准 base64 字符串;
  • 支持 url 安全模式:+-/_、省略 =

base64 解码

  • 输入 base64 编码字符串,恢复原始 byte[]
  • 自动识别并兼容标准与 url 安全字符;
  • 处理缺失或多余的填充字符,保证健壮解析;

易用 api

  • 提供静态方法:
public static string encode(byte[] data, boolean urlsafe);
public static byte[] decode(string b64);
  • 支持对 stringbyte[] 互转的简便调用;

边界与异常处理

  • null 或空输入返回空结果;
  • 对非法字符或格式抛出自定义 base64exception,并带有错误位置信息。

非功能需求

性能

  • 对大数据(如图片、视频片段)编码/解码时,避免频繁扩容,整体时空效率与 java.util.base64 相当;

可扩展性

  • 查表数组与位移逻辑解耦,后续可支持自定义字符集;

易测试

  • 附带 junit 单元测试,覆盖标准用例、url 变体、边界、非法输入等;

多线程安全

  • 算法方法无共享可变状态,可并发调用。

3. 相关技术详细介绍

base64 编码原理

  • 将每 3 个字节(24 位)划分为 4 个 6 位单元;
  • 每个 6 位取值映射到字符表 a–z, a–z, 0–9, +, /
  • 当输入长度非 3 的倍数时,使用 = 填充保证输出长度为 4 的倍数;

查表法实现

  • 预先构造长度为 64 的字符表 char[] enc = {...}
  • 解码时构造大小为 128 或 256 的反向查表 byte[] dec,映射字符到 6 位值;

位运算与字节处理

  • 使用位移与掩码操作:
int b0 = data[i]   & 0xff;
int b1 = data[i+1] & 0xff;
int b2 = data[i+2] & 0xff;
// 组合为 24 位:(b0<<16)|(b1<<8)|b2
// 依次提取 6 位输出

字符编码

  • byte[] 与 java string 互转需指定字符集(如 utf-8);

异常设计

  • 自定义运行时 base64exception,区分填充错误、非法字符、长度不匹配。

4. 实现思路详细介绍

4.1 数据结构设计

编码表

private static final char[] enc = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789+/".tochararray();
private static final char[] enc_url = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789-_".tochararray();
private static final byte[] dec = new byte[128]; // 初始化为 -1
// 遍历 enc,dec[enc[i]] = (byte)i
// 同时为 url 变体字符赋值

4.2 编码流程

初始化:选择 encenc_url

主循环:每次处理 3 字节:

  • 组合 24 位临时值;
  • 右移提取 4 个 6 位索引,访查表写入输出;

尾部处理:剩余 1 或 2 字节时,补零组合并输出相应字符,最后添加 =(标准模式);

结果拼接:使用 stringbuilder 或预估长度的 char[] 直接写入,避免扩容。

4.3 解码流程

预处理:去掉所有非 base64 字符(如换行、空格);

填充检测:记录末尾 = 数量,验证长度对 4 的整除;

主循环:每次读 4 个字符:

  • 通过 dec 查出 4 个 6 位值,组合成 24 位;
  • 拆分为最多 3 字节,依据填充数量控制输出长度;

结果返回:收集到 byte[],或使用 bytearrayoutputstream 缓冲。

4.4 api 设计

public class base64util {
    public static string encode(byte[] data);
    public static string encodeurlsafe(byte[] data);
    public static byte[] decode(string b64) throws base64exception;
}
  • encode:标准模式;
  • encodeurlsafe:url 安全模式;
  • decode:自动识别两种模式。

4.5 扩展与优化

  • 自定义字符集:支持用户传入自定义 char[]
  • 无填充模式:为极端场景去掉 =
  • 流式 api:对大文件使用输入流/输出流分块处理;
  • simd 优化:在性能敏感场景,使用 java 9+ 的 sun.misc.unsafe 或 jni 调用底层指令加速。
// ==================== 文件:base64exception.java ====================
package com.example.base64;
 
/**
 * base64 编解码异常
 */
public class base64exception extends runtimeexception {
    public base64exception(string message) {
        super(message);
    }
    public base64exception(string message, throwable cause) {
        super(message, cause);
    }
}
 
// ==================== 文件:base64util.java ====================
package com.example.base64;
 
import java.util.arrays;
 
/**
 * 基于查表法的 base64 编解码工具,支持标准与 url 安全模式
 */
public class base64util {
    // 标准 base64 编码表
    private static final char[] enc = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789+/".tochararray();
    // url 安全 base64 编码表
    private static final char[] enc_url = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789-_".tochararray();
    // 解码查表,-1 表示非法字符
    private static final byte[] dec = new byte[128];
    static {
        arrays.fill(dec, (byte)-1);
        for (int i = 0; i < enc.length; i++) {
            dec[enc[i]] = (byte)i;
        }
        for (int i = 0; i < enc_url.length; i++) {
            dec[enc_url[i]] = (byte)i;
        }
        dec['='] = 0;
    }
 
    /**
     * 标准 base64 编码
     */
    public static string encode(byte[] data) {
        return encode(data, false);
    }
 
    /**
     * url 安全 base64 编码(无填充)
     */
    public static string encodeurlsafe(byte[] data) {
        return encode(data, true);
    }
 
    private static string encode(byte[] data, boolean urlsafe) {
        if (data == null || data.length == 0) return "";
        char[] table = urlsafe ? enc_url : enc;
        int len = data.length;
        int fullgroups = len / 3;
        int remainder = len - 3 * fullgroups;
        int outlen = 4 * ((len + 2) / 3);
        stringbuilder sb = new stringbuilder(outlen);
        int idx = 0;
        // 主循环,每次处理 3 字节
        for (int i = 0; i < fullgroups; i++) {
            int b0 = data[idx++] & 0xff;
            int b1 = data[idx++] & 0xff;
            int b2 = data[idx++] & 0xff;
            sb.append(table[b0 >>> 2]);
            sb.append(table[((b0 & 0x3) << 4) | (b1 >>> 4)]);
            sb.append(table[((b1 & 0xf) << 2) | (b2 >>> 6)]);
            sb.append(table[b2 & 0x3f]);
        }
        // 处理尾部
        if (remainder == 1) {
            int b0 = data[idx++] & 0xff;
            sb.append(table[b0 >>> 2]);
            sb.append(table[(b0 & 0x3) << 4]);
            if (!urlsafe) {
                sb.append("==");
            }
        } else if (remainder == 2) {
            int b0 = data[idx++] & 0xff;
            int b1 = data[idx++] & 0xff;
            sb.append(table[b0 >>> 2]);
            sb.append(table[((b0 & 0x3) << 4) | (b1 >>> 4)]);
            sb.append(table[(b1 & 0xf) << 2]);
            if (!urlsafe) {
                sb.append('=');
            }
        }
        return sb.tostring();
    }
 
    /**
     * 自动识别标准或 url 安全 base64,解码为原始字节
     */
    public static byte[] decode(string b64) {
        if (b64 == null || b64.isempty()) return new byte[0];
        // 去除空白
        string s = b64.trim().replaceall("\\s", "");
        int len = s.length();
        if ((len & 3) != 0) {
            throw new base64exception("base64 长度非 4 的倍数: " + len);
        }
        // 计算填充数量
        int pad = 0;
        if (len > 0 && s.charat(len - 1) == '=') pad++;
        if (len > 1 && s.charat(len - 2) == '=') pad++;
        int outlen = (len * 3) / 4 - pad;
        byte[] out = new byte[outlen];
        int outidx = 0;
        int inidx = 0;
        // 主循环,每次处理 4 字符
        for (int i = 0; i < len; i += 4) {
            int c0 = chartovalue(s.charat(i));
            int c1 = chartovalue(s.charat(i+1));
            int c2 = chartovalue(s.charat(i+2));
            int c3 = chartovalue(s.charat(i+3));
            int triple = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3;
            if (outidx < outlen) out[outidx++] = (byte)(triple >> 16);
            if (outidx < outlen) out[outidx++] = (byte)(triple >> 8);
            if (outidx < outlen) out[outidx++] = (byte)triple;
        }
        return out;
    }
 
    private static int chartovalue(char c) {
        if (c >= dec.length || dec[c] < 0) {
            throw new base64exception("非法 base64 字符: " + c);
        }
        return dec[c];
    }
}
 
// ==================== 文件:testbase64util.java ====================
package com.example.base64;
 
import org.junit.jupiter.api.assertions;
import org.junit.jupiter.api.test;
 
/**
 * junit 单元测试:验证 base64 编解码功能
 */
public class testbase64util {
 
    @test
    public void teststandardencodedecode() {
        string text = "hello, 世界!";
        byte[] raw = text.getbytes(java.nio.charset.standardcharsets.utf_8);
        string enc = base64util.encode(raw);
        byte[] dec = base64util.decode(enc);
        assertions.assertequals(text, new string(dec, java.nio.charset.standardcharsets.utf_8));
    }
 
    @test
    public void testurlsafe() {
        string text = "abc+/";
        byte[] raw = text.getbytes(java.nio.charset.standardcharsets.utf_8);
        string encurl = base64util.encodeurlsafe(raw);
        assertions.assertfalse(encurl.contains("+") || encurl.contains("/"));
        byte[] dec = base64util.decode(encurl);
        assertions.assertequals(text, new string(dec, java.nio.charset.standardcharsets.utf_8));
    }
 
    @test
    public void testempty() {
        assertions.assertarrayequals(new byte[0], base64util.decode(""));
        assertions.assertequals("", base64util.encode(new byte[0]));
    }
 
    @test
    public void testinvalid() {
        assertions.assertthrows(base64exception.class, () -> base64util.decode("abcd*"));
        assertions.assertthrows(base64exception.class, () -> base64util.decode("abc"));
    }
}

5. 代码详细解读

base64exception.java

  • 自定义运行时异常,用于标识非法格式或字符错误。

base64util.java

  • 查表初始化
    • enc/enc_url 分别定义标准与 url 安全字符表;
    • dec 长度 128,预设为 -1,再给有效字符赋值;
  • 编码逻辑
    • 按 3 字节一组组合 24 位,依次右移提取 6 位索引访问 enc
    • 对尾部剩余 1 或 2 字节做特殊处理并添加 =(仅标准模式);
    • 使用 stringbuilder 预估长度,避免动态扩容。
  • 解码逻辑
    • 去掉空白和换行;
    • 验证长度为 4 的倍数并统计填充数;
    • 每 4 字符通过 dec 查表得 4×6=24 位,拆分出至多 3 字节;
    • 非法字符或不匹配抛 base64exception
  • testbase64util.java
    • 覆盖标准模式、url 安全模式、空输入和非法输入四类测试场景,确保正确性与健壮性。

6. 项目详细总结

本项目从底层位运算与查表法出发,完整实现了 base64 编解码功能,具有以下特点:

  • 深入原理:手写查表法帮助理解编码映射与填充机制;
  • 双模式支持:同时提供标准与 url 安全两种变体;
  • 高效:预分配输出缓冲、位运算提取、查表访问,性能可与 java.util.base64 媲美;
  • 健壮:对非法长度、非法字符、空白干扰等场景做严格校验并抛出友好异常;
  • 线程安全:所有方法使用局部变量,无共享可变状态,可安全并发调用;
  • 易扩展:字符表与查表逻辑解耦,可替换为自定义 base64 变体。

7. 项目常见问题及解答

q1:为什么要手写而不直接用 java.util.base64
a:手写实现有助于深入理解 base64 原理,并可在受限环境(无库支持)中使用。

q2:url 安全模式为什么不加填充?
a:jwt 等场景中常省略填充以缩短长度,读取时可自动补齐。

q3:如何支持其他字符集的 base64(如 base64url+padding)?
a:可在 encode 方法中传入自定义字符表,并在解码时提供相应反向查表。

q4:如何对大文件做流式处理?
a:可将 encode/decode 分块调用,使用 inputstream/outputstream,在每次块结束时维护少量状态。

8. 扩展方向与性能优化

simd 加速

  • 利用 java 16+ 的 vector api 对大块内存做并行位运算,提升吞吐。

jni 本地库

  • 调用 c/c++ 高性能实现,适合超大数据场景。

缓存优化

  • 对频繁相同输入做 lru 缓存,减少重复计算。

自定义变体

  • 支持 base64 feng、radix-64 等其他变体或自定义映射。

并行流水线

  • 对超大流分段并发处理,再合并结果,可充分利用多核优势。

以上就是java实现base64加解密算法的示例代码的详细内容,更多关于java base64加解密算法的资料请关注代码网其它相关文章!