一文带你读懂Base64编码
wptr33 2025-01-07 16:16 32 浏览
本文转载自微信公众号「我是开发者FTD」,作者FTD。转载本文请联系我是开发者FTD公众号。
hi,大家好,我是开发者FTD。相信很多同学在工作中,经常会用到Base64编码,那大家知道为什么会有Base64编码吗?我们为什么要使用它呢,它又是怎么实现的呢?下面就让我们来一起深入探究一下Base64编码吧。
Base 家族
在开始之前,我们先给大家介绍一下Base家族。虽然我们在工作中使用最多的是Base64,但是Base家族可不止是只有Base64,除了Base64之外,Base家族还有Base32和Base16。
我们都知道ASCII 编码,ASCII 编码是用256(2的8次方)个字符,对二进制数据进行编码的方式,同样的
- Base64 编码是用64(2的6次方)个字符,对二进制数据进行编码的方式
- Base32 编码是用32(2的5次方)个字符,对二进制数据进行编码的方式
- Base16 编码是用16(2的4次方)个字符,对二进制数据进行编码的方式
那Base家族有这么多编码形式,为什么偏偏使用Base64呢?
- Base64 编码是用64(2的6次方)个特定的ASCII字符来表示256(2的8次方)个ASCII字符,也就是说三个ASCII字符经过Base64编码后变为四个的ASCII字符显示(公约数为24),编码后数据长度比原来增加1/3,不足3n用“=”补足。
- Base32 编码就是用32(2的5次方)个特定的ASCII字符来表示256(2的8次方)个ASCII码,也就是说五个ASCII字符经过Base32编码后会变为八个ASCII字符显示(公约数为40),编码后数据长度比原来增加3/5,不足8n用“=”补足。
- Base16 编码就是用16(2的4次方)个特定的ASCII字符表示256(2的8次方)个ASCII字符,也就是说一个ASCII字符经过Base16编码后会变为两个ASCII字符显示,编码后数据长度比原来增加一倍,不足2n用“=”补足。
从上面可以看出Base64编码后,长度增加是最少的,这也是我们选用Base64的一个重要原因。
Base64 简介
Base64顾名思义,就是基于64个可打印字符来表示二进制数据的一种方法,「注意它并不是一种加密算法」。对于64个打印字符,我们只需要6个二进制位就可以完全表示了。那么我们如何利用8个二进制位来表示只需要6个二进制位就可以完全表示的可打印字符呢?由于2的6次方等于64,所以我们可以将每6个位元为一个单元,对应某个可打印字符。三个字节有24个位元,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示。
Base64是从二进制数据到字符的过程。所以计算机中所有的内容,包括文本、图片、音频、视频等等都可以使用Base64编码来表示。
Base64 编码原理
Base64编码就是使用64个字符作为一个基本字符集:
小写字母a-z、大写字母A-Z、数字0-9、符号"+"、"/"(再加上作为垫字的"=",实际上是65个字符)
然后,所有其他符号都根据一定规则转换成这个字符集中的字符。
具体来说,Base64编码的转换方式可以分为以下四步:
- 第一步,将每三个字节作为一组,一共是24个二进制位
- 第二步,将这24个二进制位分为四组,每个组有6个二进制位
- 第三步,在每组前面加两个00,扩展成32个二进制位,即四个字节
- 第四步,根据下表,得到扩展后的每个字节的对应符号,这就是Base64的编码值
Base64 编码的字符索引表如下所示:
数值 | 字符 | 数值 | 字符 | 数值 | 字符 | 数值 | 字符 |
0 | A | 16 | Q | 32 | g | 48 | w |
1 | B | 17 | R | 33 | h | 49 | x |
2 | C | 18 | S | 34 | i | 50 | y |
3 | D | 19 | T | 35 | j | 51 | z |
4 | E | 20 | U | 36 | k | 52 | 0 |
5 | F | 21 | V | 37 | l | 53 | 1 |
6 | G | 22 | W | 38 | m | 54 | 2 |
7 | H | 23 | X | 39 | n | 55 | 3 |
8 | I | 24 | Y | 40 | o | 56 | 4 |
9 | J | 25 | Z | 41 | p | 57 | 5 |
10 | K | 26 | a | 42 | q | 58 | 6 |
11 | L | 27 | b | 43 | r | 59 | 7 |
12 | M | 28 | c | 44 | s | 60 | 8 |
13 | N | 29 | d | 45 | t | 61 | 9 |
14 | O | 30 | e | 46 | u | 62 | + |
15 | P | 31 | f | 47 | v | 63 | / |
有了这个字符索引表,我们就可以把任意的二进制转换成Base64的编码了,下面我们通过几个例子,给大家展示一下转换的过程。
1,假设现在有字符串 「FTD」 需要转换成base64的编码格式
- 第一步:“F”、“T”、"D" 字符对应的ASCII码值分别为70,84,68,对应的二进制值是01000110、01010100、01000100。如图第二三行所示,由此组成一个24位的二进制字符串。
- 第二步:将24位二进制按照每6位二进制位一组分成四组。
- 第三步:在上面每一组前面补两个0,扩展成32个二进制位,此时变为四个字节:00010001、00100101、00010001、00000100。分别对应的值(Base64编码索引)为:17、37、17、4。
- 第四步:用上面的值在Base64 字符索引表中进行查找,分别对应:R、I、R、E。
因此字符串 “FTD” 经过Base64 编码之后就变为:RIRE 。
2,上面的例子中的字符正好是三个字节,如果字节数不足三个时该如何处理呢?下面我们以「F」 和 「FT」 分别举例说明如下:
如上表所示,由于字符F的二进制为01000110,按照每6位进行分组,此时只能分成一组,第二组缺少4位,如果位数不足时,用0补齐;第三组和第四组完全没有数据,则用**=「补上。因此,字符F经过Base64编码后得到的数值为」Rg==**。
3,下面我们再看一下如果只有两个字符的情况:
如上表所示,这个也属于位数不足,需要补位的情况。第一组和第二组按照正常的分组计算,第三组由于不足位数,最后两位补0,第四组完全没有数据,用**=「补上。因此,字符FT经过Base64编码后得到的数值为」RlQ=**。
关于中文的Base64编码
大家都知道中文编码有很多种,例如「GB2312、GBK、GB18030」,不同的汉字使用不同的编码格式进行编码后,它的二进制是不同的,所以在进行Base64编码后,他们的Base64编码的值也是不同的。这就要求我们在解码的时候需要注意原文的字符集格式,一定要保持一致才能正确解码。
例如:
中文 “【我是开发者FTD】公众号” UTF-8 格式的Base64 编码后的值是:44CQ5oiR5piv5byA5Y+R6ICFRlRE44CR5YWs5LyX5Y+3
中文 “【我是开发者FTD】公众号” GB2312 格式的Base64 编码后的值是:ob7O0srHv6q3otXfRlREob+5q9bausU=
Base64 是加密算法吗?
Base64 主要不是用来加密的,它主要的用途是把一些二进制数转成普通字符用于网络传输,这是因为一些二进制字符在传输协议中属于控制字符,不能直接在网络上传输。另外,还有一些系统中只能使用ASCII字符。Base64 编码就是用来将非ASCII字符的数据转换成ASCII字符的一种方法。Base64 并不是安全领域下的加密解密算法,虽然有时候也会经常看到所谓的Base64加密解密算法。其实Base64只能算是一个编码算法,对数据内容进行编码来适合网络传输。虽然Base64编码过后原文也变成无法直接理解的字符格式,但是这种编码方式比较初级,很简单,很容易就可以被还原成原文,所以如果有比较重要的信息需要加密,一定要使用我们之前文章中介绍的那些加密算法进行数据的安全保护。
Base64 编码实现
Java语言中有多个库实现了Base64编码,不管哪一个库,最终的结果都是一样的。
JDK 提供的 Base64 编码实现:
public static String encode(String data) {
return Base64.getEncoder().encodeToString(data.getBytes());
}
public static String decode(String base64Data) {
return new String(Base64.getDecoder().decode(base64Data));
}
Bouncy Castle 提供的 Base64 编码实现:
public static String encode(String data) {
return new String(Base64.encode(data.getBytes()));
}
public static String decode(String base64Data) {
return new String(Base64.decode(base64Data));
}
Commons Codec 提供的 Base64 编码实现:
public static String encode(String data) {
return Base64.encodeBase64String(data.getBytes());
}
public static String decode(String base64Data) {
return new String(Base64.decodeBase64(base64Data));
}
下面让我们用Java语言的实现来验证一下,我们第二章节的推理是否正确吧,代码如下:
public static void main(String[] args) {
String ftd = "FTD";
String ft = "FT";
String f = "F";
System.out.println("FTD base64 编码:" + encode(ftd));
System.out.println("FT base64 编码:" + encode(ft));
System.out.println("F base64 编码:" + encode(f));
}
输出结果为:
FTD base64 编码:RlRE
FT base64 编码:RlQ=
F base64 编码:Rg==
可以看到,和我们分析所得的结果是完全一样的。
查看完整代码请访问:
https://github.com/ForTheDevelopers/JavaSecurity
总结
Base64是我们在工作中经常用到,但是很少有人会深入研究一下它的实现原理,如果理解不当,甚至可能还会有人用它当做加解密用到业务系统关键位置,可能会引发比较严重的后果,相信大家看完上述的内容后,应该对Base64编码已经有了深刻的理解了吧。
相关推荐
- [常用工具] git基础学习笔记_git工具有哪些
-
添加推送信息,-m=messagegitcommit-m“添加注释”查看状态...
- centos7安装部署gitlab_centos7安装git服务器
-
一、Gitlab介1.1gitlab信息GitLab是利用RubyonRails一个开源的版本管理系统,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。...
- 太高效了!玩了这么久的Linux,居然不知道这7个终端快捷键
-
作为Linux用户,大家肯定在Linux终端下敲过无数的命令。有的命令很短,比如:ls、cd、pwd之类,这种命令大家毫无压力。但是,有些命令就比较长了,比如:...
- 提高开发速度还能保证质量的10个小窍门
-
养成坏习惯真是分分钟的事儿,而养成好习惯却很难。我发现,把那些对我有用的习惯写下来,能让我坚持住已经花心思养成的好习惯。...
- 版本管理最好用的工具,你懂多少?
-
版本控制(Revisioncontrol)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术。...
- Git回退到某个版本_git回退到某个版本详细步骤
-
在开发过程,有时会遇到合并代码或者合并主分支代码导致自己分支代码冲突等问题,这时我们需要回退到某个commit_id版本1,查看所有历史版本,获取git的某个历史版本id...
- Kubernetes + Jenkins + Harbor 全景实战手册
-
Kubernetes+Jenkins+Harbor全景实战手册在现代企业级DevOps体系中,Kubernetes(K8s)、Jenkins和Harbor组成的CI/CD流水...
- git常用命令整理_git常见命令
-
一、Git仓库完整迁移完整迁移,就是指,不仅将所有代码移植到新的仓库,而且要保留所有的commit记录1.随便找个文件夹,从原地址克隆一份裸版本库...
- 第三章:Git分支管理(多人协作基础)
-
3.1分支基本概念分支是Git最强大的功能之一,它允许你在主线之外创建独立的开发线路,互不干扰。理解分支的工作原理是掌握Git的关键。核心概念:HEAD:指向当前分支的指针...
- 云效Codeup怎么创建分支并进行分支管理
-
云效Codeup怎么创建分支并进行分支管理,分支是为了将修改记录分叉备份保存,不受其他分支的影响,所以在同一个代码库里可以同时进行多个修改。创建仓库时,会自动创建Master分支作为默认分支,后续...
- git 如何删除本地和远程分支?_git怎么删除远程仓库
-
Git分支对于开发人员来说是一项强大的功能,但要维护干净的存储库,就需要知道如何删除过时的分支。本指南涵盖了您需要了解的有关本地和远程删除Git分支的所有信息。了解Git分支...
- git 实现一份代码push到两个git地址上
-
一直以来想把自己的博客代码托管到github和coding上想一次更改一次push两个地址一起更新今天有空查资料实践了下本博客的github地址coding的git地址如果是Gi...
- git操作:cherry-pick和rebase_git cherry-pick bad object
-
在编码中经常涉及到分支之间的代码同步问题,那就需要cherry-pick和rebase命令问题:如何将某个分支的多个commit合并到另一个分支,并在另一个分支只保留一个commit记录解答:假设有两...
- 模型文件硬塞进 Git,GitHub 直接打回原形:使用Git-LFS管理大文件
-
前言最近接手了一个计算机视觉项目代码是屎山就不说了,反正我也不看代码主要就是构建一下docker镜像,测试一下部署的兼容性这本来不难但是,国内服务器的网络环境实在是恶劣,需要配置各种镜像(dock...
- 防弹少年团田柾国《Euphoria》2周年 获世界实时趋势榜1位 恭喜呀
-
当天韩国时间凌晨3时左右,该曲在Twitter上以“2YearsWithEuphoria”的HashTag登上了世界趋势1位。在韩国推特实时趋势中,从上午开始到现在“Euphoria2岁”的Has...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
程序员的开源月刊《HelloGitHub》第 71 期
-
详细介绍一下Redis的Watch机制,可以利用Watch机制来做什么?
-
假如有100W个用户抢一张票,除了负载均衡办法,怎么支持高并发?
-
Java面试必考问题:什么是乐观锁与悲观锁
-
如何将AI助手接入微信(打开ai手机助手)
-
SparkSQL——DataFrame的创建与使用
-
redission YYDS spring boot redission 使用
-
一文带你了解Redis与Memcached? redis与memcached的区别
-
如何利用Redis进行事务处理呢? 如何利用redis进行事务处理呢英文
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)