base64 笔记

Base64 原理

Base64编码是使用64个可打印ASCII字符(A-Z、a-z、0-9、+、/)将任意字节序列数据编码成ASCII字符串,另有“=”符号用作后缀用途。

Base64将输入字符串按字节切分,取得每个字节对应的二进制值(若不足8比特则高位补0),然后将这些二进制数值串联起来,再按照6比特一组进行切分(因为2^6=64),最后一组若不足6比特则末尾补0。将每组二进制值转换成十进制,然后在上述表格中找到对应的符号并串联起来就是Base64编码结果。

由于二进制数据是按照8比特一组进行传输,因此Base64按照6比特一组切分的二进制数据必须是24比特的倍数(6和8的最小公倍数)。24比特就是3个字节,若原字节序列数据长度不是3的倍数时且剩下1个输入数据,则在编码结果后加2个=;若剩下2个输入数据,则在编码结果后加1个=。

完整的Base64定义可见RFC1421和RFC2045。因为Base64算法是将3个字节原数据编码为4个字节新数据,所以Base64编码后的数据比原始数据略长,为原来的4/3。在电子邮件中,根据RFC822规定,每76个字符,还需要加上一个回车换行。可以估算编码后数据长度大约为原长的135.1%。

Base64可用于任意数据的底层二进制数据编码,以应用于只能传输ASCII字符的场合。不过最常用于文本数据的处理传输,例如在MIME格式的电子邮件中,Base64可以用来编码邮件内容,方便在不同语言计算机间传输而不乱码,注意是传输而不是显示,例如在西欧地区计算机上使用UTF-8编码即可正常显示中文(安装有对应字库),但是它未必能正常传输中文,这时转换为Base64便无此顾虑。

Base64编码若无特别说明,通常约定非ASCII字符按照UTF-8字符集进行编码处理。

base64编码编码Uuencode编码类似,但base64将0~63之间的数字通过查表换字符,所以可能存在不用默认表而用另一个表进行base64编码加密

C++ 实现

加密 - Encode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
string encode(const string& plain)
{
// 转码表
static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// 计算加密字符数
size_t count = plain.size() / 3;
size_t remain = plain.size() % 3;
string result(count * 4 + (remain ? 4 : 0), 0);
size_t i = 0;

while (i != count)
{
// 3 bytes 为一组进行加密
// 取第一个 byte 的前 6 位
result[i * 4] = table[plain[i * 3] >> 2];
// 取第一个 byte 的后 2 位与第二个 byte 的前 4 位组合
result[i * 4 + 1] = table[((plain[i * 3] & 0x3) << 4) | (plain[i * 3 + 1] >> 4)];
// 取第二个 byte 的后 4 位与第三个 byte 的前 2 位组合
result[i * 4 + 2] = table[((plain[i * 3 + 1] & 0xf) << 2) | (plain[i * 3 + 2] >> 6)];
// 取第三个 byte 的后 6 位
result[i * 4 + 3] = table[plain[i * 3 + 2] & 0x3f];

// 迭代至下一个 3 bytes
++i;
}

// 若多出 1 byte
if (remain == 1)
{
// 取 byte 前 6 位
result[i * 4] = table[plain[i * 3] >> 2];
// 取 byte 后 2 位,剩余 4 位补 0
result[i * 4 + 1] = table[(plain[i * 3] & 0x3) << 4];
// 剩余补 '=' 凑满 4 的倍数长度
result[i * 4 + 2] = result[i * 4 + 3] = '=';
}
// 若多出 2 bytes
else
{
// 取第一个 byte 前 6 位
result[i * 4] = table[plain[i * 3] >> 2];
// 取第一个 byte 的后 2 位与第二个 byte 的前 4 位组合
result[i * 4 + 1] = table[((plain[i * 3] & 0x3) << 4) | (plain[i * 3 + 1] >> 4)];
// 取第二个 byte 的后 4 位,剩余 2 位补 0
result[i * 4 + 2] = table[(plain[i * 3 + 1] & 0xf) << 2];
// 剩余补 '=' 凑满 4 的倍数长度
result[i * 4 + 3] = '=';
}

return result;
}

解密 - Decode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
string decode(const string& code)
{
// 映射表
static char table[] = {
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64
};
static string bad("decode bad!");

// 如果长度不是 4 的倍数,则返回 bad 字符串
if ((code.size() & 0x3) != 0) return bad;

// 计算解密字符
size_t count = code.size() / 4;
string result(count * 3, 0);
size_t i = 0;
char h, l;

while (i != count)
{
// 以 4 bytes 为一组进行解密
// 取映射表对应数 (每个数作 6 bit 计算)
h = table[code[i * 4]];
l = table[code[i * 4 + 1]];
// 若一组中前两个字符不在 base64 加密字符范围,则返回 bad 字符串
if (h == 64 || l == 64) return bad;
// 将第一个数的前 4 bit 与第二个数的前 4 bit 组合
result[i * 3] = (h << 2) | (l >> 4);

// 取映射表对应数
h = table[code[i * 4 + 2]];
// 若一组中第三个数不在 base64 加密字符范围,则说明为 '=',即解密结束 (严格来说需判断是否为'=')
if (h != 64)
{
// 将第二个数的前 2 bit 与第三个数的前 4 bit 组合
result[i * 3 + 1] = (l << 4) | (h >> 2);

// 取映射表对应数
l = table[code[i * 4 + 3]];
if (l != 64)
{
// 将第三个数的前 2 bit 与第二个数的 6 bit 组合
result[i * 3 + 2] = (h << 6) | l;
}
else
{
break;
}
}
else
{
break;
}
// 迭代至下三个数 (每个数作 6 bit 计算)
++i;
}
return result;
}

Python 实现

1
2
3
4
5
6
7
import base64

# encode
base64.b64encode("Duzou")

# decode
base64.b64decode("RHV6b3VfMXNfczBfaDRuZHNPbWU=")

bash

加密 - Encode

1
echo Duzou | base64

注意,bash 的 echo 会默认加一个回车在末尾

解密 - Decode

1
echo RHV6b3VfMXNfczBfaDRuZHNPbWU= | base64 -d

在线网站

  • https://www.qqxiuzi.cn/bianma/base64.htm