绪论
天下编码规则千千万,而base64则是网络上使用的最为广泛的一种用于传输8Bit字节的编码方式之一,Base64编码是从二进制到字符的过程,可用于在HTTP环境下传递较长的标识信息。采用Base64编码具有不可读性,需要解码后才能阅读
而正巧这几天做moeCTF的时候有一道题涉及到了base64,所以今天我想写一篇博文来讲讲究竟什么是base64
豆知识:其实一般我都是断断续续几天写完一篇的…
基本原理
加密
一、密钥
正所谓“?如其名”,base64编码的加密过程正是基于一个64位的密钥进行的,转换的对应规则如下表:
原值 | 转换所得字符 | 原值 | 转换所得字符 | 原值 | 转换所得字符 | 原值 | 转换所得字符 |
---|---|---|---|---|---|---|---|
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”:
char key_form[64]={
0};
...
int main(void)
{
...
ifstream key_in;
key_in.open("key.txt",ios::in);
char ch;
for(int i=0;i<64;i++)
key_in>>key_form[i];
也许没了解过base64的朋友会对转化表感觉很疑惑,哪怕是最基础的ASCII都是有128个字符,那么这只有64个对应关系的密钥真的够用吗?
但是相信不少人在看到64的时候便已经有一定的想法了:
64=26(base64密钥)
128=28 (ASCII)
你想到了什么呢?
二、转换规则:“三变四,按表转”
我们都知道,通常在计算机当中储存的字符,一个字符(char)要占用一个字节(byte),即8个二进制位(bit)
豆知识:汉字编码一般根据GB2312-80标准进行,一个汉字占两个字节
比如说字符’A’,她的ASCII码是65,即在内存当中储存的字符为’A’的变量实际的值为65,她在内存里长这个样子:
文本 | A |
---|---|
ASCII码 | 65 |
二进制位 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
---|
豆知识:在CSDN上画表格是一件十分麻烦且艹蛋的事情
我们以三个字符"ABC"作为例子:
文本 | A | B | C |
---|---|---|---|
ASCII码 | 65 | 66 | 67 |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
---|
(上面空的格仅在本文中用作表示分隔,在内存中并不存在)
我们以每三个字符作为一组,一共24个二进制位,重新进行分组
重新分组时我们以每6个二进制位作为一组,共分为四组:
0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
---|
最后在每组的高位补上两个0,变成标准的一个字节:
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
---|
于是转化后内存当中的值变为了:
16 | 20 | 9 | 3 |
---|
转换后呢?我们直接将这个值进行输出吗?这四个字符都是特殊类型字符,又如何在文本当中输出呢?
别忘了我们还有密码表啊,base64之所以叫base64可不就是因为有一份64个对应关系的密码表么?
我们将这些值按照密码表上的对应关系进行转换,最终输出的文本应该是:
Q | U | J | D |
---|
相信大家看到这里也应该明白为什么密钥是有着64个对应关系的了,六位的一个无符号整型所能表示的数据范围不就是0~63、刚好是64个数么?
三、非3倍数字节文本:“尾补0,余补=”
或许有的人要问:如果字节数并不是三个字节,该如何进行编码呢?
无论是剩余一个字节,还是剩余两个字节,都无法满足基础的base64编码的要求
base64编码标准对于这种特殊情况的处理标准是:
对于多余的一个字符,在末尾补上4个0,使其能被分为两个6位的量,并在最终输出的文本当中补充两个’='字符
对于多余的两个字符,在末尾补上2个0,使其能被分为三个6位的量,并在最终输出的文本当中补充一个’='字符
如下图所示(图片来自网络,如有侵权请第一时间联系我):
四、代码实现
base64加密最终的C++代码实现如下:
void base_64(void)
{
char ch;
for(i=0;(ch=getchar())!=EOF;i+=8)//此处使用freopen()函数进行重定向以从文件中读取,若是从标准输入流读取一行字符串的话也可以把判定条件变为'\n'而不是EOF
{
for(j=7;j>=0;j--)//转换为二进制进行运算
{
plaintext[i+j]=ch&1;
ch>>=1;
}