Java实现加密(五)Base64编码

news/2024/7/24 13:30:54 标签: java, 开发语言

目录

    • 一、Base64是怎么诞生的
    • 二、Base64定义
    • 三、Base64原理
      • 1.ASCII码转Base64(字节数 % 3 == 0)
      • 2.ASCII码转Base64(字节数 % 3 == 2)
      • 3.ASCII码转Base64(字节数 % 3 == 1)
      • 4.UTF-8转Base64
    • 四、Java实现Base64编解码
      • 1.方法一:DatatypeConverter(JDK6)
      • 2.方法二:Base64(JDK8)
      • 3.方法三:commons工具包
    • 五、总结
    • 六、补充:JS实现Base64编解码

Base64 在线编码/解码: https://base64.us/

一、Base64是怎么诞生的

  • 互联网发展早期,电子邮件是最有效的应用。
  • 电子邮件的 SMTP 传输协议在早期,只能用于传送 7 位的 ASCII 码,而ASCII码就是基于英语设计的,对于非英语国家的文字等资源就无法发送。
  • 为了解决这个问题,后来有了通用互联网邮件扩充协议 MIME ,这是一种用于在互联网上传输数据的标准,增加了邮件主题结构,定义了邮件、文本、音频、图像和多媒体文件等非ASCII码的编码传输规则。
  • Base64是MIME的一种编码方式。MIME规范中定义了Base64编码作为一种可靠的方式,用于在文本协议中表示二进制数据。通过使用Base64编码,可以将二进制数据转换为可打印字符,从而保证数据在传输过程中不受损失,并且能够被各种文本协议(如SMTP、HTTP)正确处理。
  • MIME中,当需要在文本协议中传输二进制数据(如邮件附件或图片数据)时,通常会将数据先进行Base64编码,然后将编码后的数据作为文本内容进行传输。

二、Base64定义

Base64 是一种用64个字符(a-z, A-Z, 0-9, +, /)来表示任意二进制数据的方法。

Base64 是一种索引编码,由于 2^6=64,所以每 6 个比特为一个单元,对应某个可打印字符,每个字符都对应一个索引。

索引和打印字符的对应关系如下:

索引对应字符索引对应字符索引对应字符索引对应字符
0A16Q32g48w
1B17R33h49x
2C18S34i50y
3D19T35j51z
4E20U36k520
5F21V37l531
6G22W38m542
7H23X39n553
8I24Y40o564
9J25Z41p575
10K26a42q586
11L27b43r597
12M28c44s608
13N29d45t619
14O30e46u62+
15P31f47v63/

三、Base64原理

Base64 编码要求把 3 个 8 位的字节(3*8=24)转化为 4 个 6 位的字节(4*6=24),之后在 6 位的前面补两个 0,形成 8 位一个字节的形式。 如果剩下的字符不足 3 个字节,则用 0 填充,输出字符使用 =,因此编码后输出的文本末尾可能会出现 1 或 2 个 =。

为了保证所输出的编码位可读字符,Base64 制定了一个编码表,以便进行统一转换。编码表的大小为 2^6=64,这也是 Base64 名称的由来。

1.ASCII码转Base64(字节数 % 3 == 0)

ASCII是8位一个字节,Base64是6位一个字节,

3个字节的ASCII编码刚好等于4个字节的Base64编码,3 * 8 = 4 * 6 = 24

示例:

  • ASCII编码:you
  • Base64编码:eW91

在这里插入图片描述

2.ASCII码转Base64(字节数 % 3 == 2)

ASCII是8位一个字节,Base64是6位一个字节,

2个字节的ASCII编码补2位零等于3个字节的Base64编码,不足4个字节的Base64编码使用 = 代替一个字节来补齐,2 * 8 + 2 = 3 * 6 = 18

示例:

  • ASCII编码:yo
  • Base64编码:eW8=

在这里插入图片描述

3.ASCII码转Base64(字节数 % 3 == 1)

ASCII是8位一个字节,Base64是6位一个字节,

1个字节的ASCII编码补4位零等于2个字节的Base64编码,不足4个字节的Base64编码使用 = 代替一个字节来补齐,1 * 8 + 4 = 2 * 6 = 12

示例:

  • UTF8编码:y
  • Base64编码:eQ==

在这里插入图片描述

4.UTF-8转Base64

UTF-8是8位一个字节,Base64是6位一个字节,

UTF-8里中文是三个字节,所以一个UTF-8编码的中文刚好可以转换为4个字节的Base64编码。3 * 8 = 4 * 6 = 24

示例:

  • ASCII编码:
  • Base64编码:5Lit

首先,由于系统中默认字符串的编码格式为 unicode,需要将字符串 转换为 utf-8 格式的二进制数组:

java">/**
 * 字节数组 转 二进制字符串
 * @param bytes 高位 到 低位
 * @return 二进制字符串
 */
public static String byteToBinStr2(byte[] bytes) {
    StringBuilder s1 = new StringBuilder();
    for (byte aByte : bytes) {
        s1.append(Long.toBinaryString((aByte & 0xFF) + 0x100).substring(1));
    }
    return s1.toString();
}

public static void main(String[] args) {
    System.out.println(byteToBinStr2("中".getBytes(StandardCharsets.UTF_8)));
}

执行结果:

在这里插入图片描述

每8位1字节分隔得到:

11100100 10111000 10101101

Base64编码:

在这里插入图片描述


四、Java实现Base64编解码

1.方法一:DatatypeConverter(JDK6)

使用 jdk 自带的 DatatypeConverter.java 类实现,但是 jdk 版本必须 >= 1.6

java">import java.io.UnsupportedEncodingException;
import javax.xml.bind.DatatypeConverter;

编码:

java">/**
 * base64 编码(方法一)
 * @explain DatatypeConverter.java实现
 * @param str 待编码字符串
 * @return 编码字符串
 */
public static String encode(String str) {
    // base64字符串
    String base64Str = "";
    try {
        // 非字符串才进行编码
        if (str != null && str.length() > 0) {
        	// String 转 byte[]
        	byte[] bytes = str.getBytes("utf-8");
            // 编码
            base64Str = DatatypeConverter.printBase64Binary(bytes);
        }
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return base64Str;
}

解码:

java">/**
 * base解码(方法一)
 * @explain DatatypeConverter.java实现
 * @param base64Str 待解码字符串
 * @return 解码字符串
 */
public static String decode(String base64Str) {
    // 解码后的字符串
    String str = "";
    // 非空字符串才进行解码
    if (base64Str != null && base64Str.length() > 0) {
        // 解码
        byte[] base64Bytes = DatatypeConverter.parseBase64Binary(base64Str);
        try {
            // byte[] 转 String
            str = new String(base64Bytes, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
    return str;
}

测试代码:

java">public static void main(String[] args) {
    String s = "y";
    String base64Str = encode2(s);
    System.out.println("原文:" + s);
    System.out.println("base64编码后(方法一):" + base64Str);
    System.out.println("base64解码后(方法一):" + decode2(base64Str));
}

执行结果:

在这里插入图片描述

2.方法二:Base64(JDK8)

使用 jdk 自带的 Base64.java 类实现,但是 jdk 版本必须 >= 1.8

java">import java.util.Base64;
import java.nio.charset.StandardCharsets;

编码:

java">/**
 * base64编码(方法二)
 * @explain Base64.java实现
 * @param str 待编码字符串
 * @return 编码字符串
 */
public static String encode2(String str) {
    // 非空字符串才进行编码
    if (str != null && str.length() > 0) {
        // String 转 byte[]
        byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
        // 编码(base64字符串)
        return Base64.getEncoder().encodeToString(bytes);
    }
    return "";
}

解码:

java">/**
 * base64解码(方法二)
 * @explain Base64.java实现
 * @param base64Str 待解码字符串
 * @return 解码字符串
 */
public static String decode2(String base64Str) {
    // 非空字符串才进行解码
    if (base64Str != null && base64Str.length() > 0) {
        // 编码
        byte[] base64Bytes = Base64.getDecoder().decode(base64Str);
        // byte[] 转 String(解码后的字符串)
        return new String(base64Bytes, StandardCharsets.UTF_8);
    }
    return "";
}

测试代码:

java">public static void main(String[] args) {
    String s = "y";
    String base64Str = encode2(s);
    System.out.println("原文:" + s);
    System.out.println("base64编码后(方法二):" + base64Str);
    System.out.println("base64解码后(方法二):" + decode2(base64Str));
}

执行结果:

在这里插入图片描述

3.方法三:commons工具包

<dependency>
	<groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.15</version>
</dependency>
java">import org.apache.commons.codec.binary.Base64;
import java.nio.charset.StandardCharsets;

编码:

java">/**
 * base64编码(方法三)
 * @explain commons工具包实现
 * @param str 待编码字符串
 * @return 编码字符串
 */
public static String encode3(String str) {
    // 非空字符串才进行编码
    if (str != null && str.length() > 0) {
        // String 转 byte[]
        byte[] bytes =  str.getBytes(StandardCharsets.UTF_8);
        // 编码(base64字符串)
        return Base64.encodeBase64String(bytes).replaceAll(" \r\n", "");
    }
    return "";
}

解码:

java">/**
 * base64解码(方法三)
 * @explain commons工具包实现
 * @param base64Str 待解码字符串
 * @return 解码字符串
 */
public static String decode3(String base64Str) {
    // 非空字符串才进行解码
    if (base64Str != null && base64Str.length() > 0) {
        // 解码
        byte[] base64Bytes = Base64.decodeBase64(base64Str);
        // byte[] 转 String(解码后的字符串)
        return new String(base64Bytes, StandardCharsets.UTF_8);
    }
    return "";
}

测试代码:

java">public static void main(String[] args) {
    String s = "y";
    String base64Str = encode3(s);
    System.out.println("原文:" + s);
    System.out.println("base64编码后(方法三):" + base64Str);
    System.out.println("base64解码后(方法三):" + decode3(base64Str));
}

执行结果:

在这里插入图片描述


五、总结

执行结果:方法一、方法二、方法三一致

效率:快–>慢:方法二 > 方法一 > 方法三

因此,如果项目用的是 jdk1.8,最佳选择是方法二;jdk1.6,最佳选择是方法一。


六、补充:JS实现Base64编解码

JavaScript 提供了两个原生的方法,用来处理Base64编码:btoa()atob()

  • btoa():Base64编码。
  • atob():Base64解码。

以上两个方法如果操作的字符串不是 ASCII 编码会报错:

例如:btoa('中')

VM707:1 Uncaught DOMException: Failed to execute ‘btoa’ on ‘Window’: The string to be encoded contains characters outside of the Latin1 range.

在这里插入图片描述

可以使用这两个方法,将非ASCII码的字符作为URI组件进行编码,然后再进行Base64编码。

  • encodeURIComponent():作为URI组件进行编码。
  • decodeURIComponent():作为URI组件进行解码。

综上所述,适用于所有情况(ASCII码+非ASCII码)的Base64编解码方法如下:

  • btoa(encodeURIComponent(待编码内容)):Base64 编码。
  • decodeURIComponent(atob(待解码内容)):Base64 解码。

注意:这种方法和UTF-8格式的加密不能通用。

执行结果:

在这里插入图片描述

整理完毕,完结撒花~ 🌻





参考地址:

1.Base64编码知识详解,https://baijiahao.baidu.com/s?id=1735577033729027737&wfr=spider&for=pc

2.什么是base64,https://zhuanlan.zhihu.com/p/76666060

3.Base64 编码/解码 | 菜鸟工具,https://c.runoob.com/front-end/693/

4.java base64编码、解码的三种方式,https://blog.51cto.com/u_15964717/6093954


http://www.niftyadmin.cn/n/456539.html

相关文章

C语言实现排序算法的六种方式

1、冒泡法 void Sort(int *pData,int count) {int temp;int i,j;for(i0;i<count;i){for(j0;j<count-i-1;j){if(pData[j]>pData[j1]){temppData[j];pData[j]pData[j1];pData[j1]temp;}}} }int main() {int data[]{10,9,8,7,6,5,4}; Sort(data,7);int i;for(i0;i<7;…

Spark中python和jvm的通信杂谈--ArrowConverter

背景 要提起ArrowConverters&#xff0c;就得说起Arrow这个项目&#xff0c;该项目的初衷是加速进程间的数据交换&#xff0c;从目前的社区发展以及它的周边来看&#xff0c;其实是一个很不错的项目。 那为什么Spark要引入Arrow呢&#xff1f;其实还得从Pyspark中python和jvm的…

QT学习之旅 - network连接

文章目录 网络知识点IP地址IPv4和IPv6 端口号(协议端口)端口分类UDP端口和TCP端口 networkpro文件.h文件.cpp文件 UDP连接绑定端口绑定成功后等待对方进行连接点击发送源码扩展: nodejs-udp服务端(用于跟QT程序进行通信)现象 网络知识点 IP地址 192.168.127.170(√) 192.168.…

侯捷——1.C++面向对象高级开发 总结

侯捷——1.C面向对象高级开发 总结 前面的几个视频没有总结&#xff0c;等以后有空再补 7. Class with pointer member(s) —— string 类 该string类&#xff0c;内含指针&#xff0c;所以要自己写构造函数和析构函数&#xff0c;不能使用默认的构造函数和析构函数。 包含…

Codeforces Round 877 (Div. 2) A~D

Codeforces Round 877 (Div. 2) A. Blackboard List 题意&#xff1a;给两个数&#xff0c;两个差的绝对值为新数&#xff0c;现给出最后的结果&#xff0c;问一开始的两个数的其中一个为什么。 思路&#xff1a;首先负数无法通过绝对值得出&#xff0c;因此如果有负数就选负…

Doris-简介、架构、编译、安装和数据表的基本使用

目录 1、Doris简介2、Doris网址3、Doris架构3、编译和安装 3.1、软硬件需求3.2、编译 3.2.1、安装Docker环境3.2.2、使用Docker 开发镜像编译3.3、集群部署 3.3.1、创建目录并拷贝编译后的文件3.3.2、部署 FE 节点3.3.3、配置 BE 节点3.3.4、在 FE 中添加所有 BE 节点3.3.5、启…

Elasticsearch 分词器

前奏 es的chinese、english、standard等分词器对中文分词十分不友好&#xff0c;几乎都是逐字分词&#xff0c;对英文分词比较友好。 在kibana的dev tools中测试分词&#xff1a; POST /_analyze {"analyzer": "standard","text": "你太…

Debian12中Grub2识别Windows

背景介绍&#xff1a;windows10 debian11,2023年6月&#xff0c;Debian 12正式版发布了。抵不住Debian12新特性的诱惑&#xff0c;我将Debian11升级至Debian12。升级成功&#xff0c;但Debian12的Grub2无法识别Window10。于是执行如下命令&#xff1a; debian:~# update-grub G…