Base64编码原理
Base64作用
目前Base64已经成为网络上常见的传输8Bit字节代码的编码方式之一。在做支付系统时,系统之间的报文交互都需要使用Base64对明文进行转码,然后再进行签名或加密,之后再进行(或再次Base64)传输。
Base64编码原理
Base64的原理比较简单,每当我们使用Base64时都会先定义一个类似这样的数组:
['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']
上面就是Base64的索引表,字符选用了"A-Z、a-z、0-9、+、/" 64个可打印字符,这是标准的Base64协议规定。在日常使用中我们还会看到“=”或“==”号出现在Base64的编码结果中,“=”在此是作为填充字符出现,后面会讲到。
具体转换步骤
- 第一步,将待转换的字符串每三个字节分为一组,每个字节占8bit,那么共有24个二进制位。
- 第二步,将上面的24个二进制位每6个一组,共分为4组。
- 第三步,在每组前面添加两个0,每组由6个变为8个二进制位,总共32个二进制位,即四个字节。
- 第四步,根据Base64编码对照表(见下图)获得对应的值。
0 A 17 R 34 i 51 z1 B 18 S 35 j 52 02 C 19 T 36 k 53 13 D 20 U 37 l 54 24 E 21 V 38 m 55 35 F 22 W 39 n 56 46 G 23 X 40 o 57 57 H 24 Y 41 p 58 68 I 25 Z 42 q 59 79 J 26 a 43 r 60 810 K 27 b 44 s 61 911 L 28 c 45 t 62 +12 M 29 d 46 u 63 /13 N 30 e 47 v14 O 31 f 48 w 15 P 32 g 49 x16 Q 33 h 50 y
从上面的步骤我们发现:
- Base64字符表中的字符原本用6个bit就可以表示,现在前面添加2个0,变为8个bit,会造成一定的浪费。因此,Base64编码之后的文本,要比原文大约三分之一。
- 为什么使用3个字节一组呢?因为6和8的最小公倍数为24,三个字节正好24个二进制位,每6个bit位一组,恰好能够分为4组。
示例说明
以下图的表格为示例,我们具体分析一下整个过程。
- 第一步:“M”、“a”、"n"对应的ASCII码值分别为77,97,110,对应的二进制值是01001101、01100001、01101110。如图第二三行所示,由此组成一个24位的二进制字符串。
- 第二步:如图红色框,将24位每6位二进制位一组分成四组。
- 第三步:在上面每一组前面补两个0,扩展成32个二进制位,此时变为四个字节:00010011、00010110、00000101、00101110。分别对应的值(Base64编码索引)为:19、22、5、46。
- 第四步:用上面的值在Base64编码表中进行查找,分别对应:T、W、F、u。因此“Man”Base64编码之后就变为:TWFu。
位数不足情况
上面是按照三个字节来举例说明的,如果字节数不足三个,那么该如何处理?
- 两个字节:两个字节共16个二进制位,依旧按照规则进行分组。此时总共16个二进制位,每6个一组,则第三组缺少2位,用0补齐,得到三个Base64编码,第四组完全没有数据则用“=”补上。因此,上图中“BC”转换之后为“QKM=”;
- 一个字节:一个字节共8个二进制位,依旧按照规则进行分组。此时共8个二进制位,每6个一组,则第二组缺少4位,用0补齐,得到两个Base64编码,而后面两组没有对应数据,都用“=”补上。因此,上图中“A”转换之后为“QQ==”;
注意事项
- 大多数编码都是由字符串转化成二进制的过程,而Base64的编码则是从二进制转换为字符串。与常规恰恰相反,
- Base64编码主要用在传输、存储、表示二进制领域,不能算得上加密,只是无法直接看到明文。也可以通过打乱Base64编码来进行加密。
- 中文有多种编码(比如:utf-8、gb2312、gbk等),不同编码对应Base64编码结果都不一样。
Base64代码实现
- python
class MyBase64():base64_dict = {}string_temp = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ''abcdefghijklmnopqrstuvwxyz''0123456789+/')ascii_string = ''.join([chr(i) for i in range(4, 2 ** 7 - 1)])def __init__(self, string):# 初始化,创建 base64 编码字典self.string = stringfor i in range(2 ** 6):self.base64_dict[i] = self.string_temp[i]def convert(self):# base64 编码过程# 编码string_encode_byte = self.string.encode('utf-8')# 十进制化string_digit_list = list(string_encode_byte)# 二进制化 + 0 填充string_bin_list = []for item in string_digit_list:string_bin_list.append(str(bin(item))[2:].zfill(8))# 字符串合并string_sum = ''.join(string_bin_list)# 6 的倍数,不足 0 填充string_fill = self.fillIt(string_sum, factor=6, item='0')# 切片,6位一个单位string_bin_list2 = self.splitIt(string_fill, bits=6)# 十进制化string_digit_list2 = []for item in string_bin_list2:string_digit_list2.append(int(item, 2))# 查表string_base64_list = []for item in string_digit_list2:string_base64_list.append(self.base64_dict[item])# 拼接string_sum2 = ''.join(string_base64_list)# 4 的倍数,不足填充 =string_convert = self.fillIt(string_sum2, factor=4, item='=')return string_convertdef fillIt(self, string, factor, item):"""指定倍数填充指定字符string:原字符串factor:倍数item:填充字符"""length = len(string)remainder = length % factorif remainder:times = factor - remainderstring = string + times * itemreturn stringdef splitIt(self, string, bits):"""指定位数切片string:原字符串bits:每次切片数量"""length = len(string)new_list = []for i in range(bits, length + 1, bits):new_list.append(string[i - bits:i])remain = length % bitsif remain != 0:new_list.append(string[-remain:])return new_listif __name__ == '__main__':string = input()myBase64 = MyBase64(string)enc_string = myBase64.convert()print("测试字符串:{}".format(string))print("base64:{}".format(enc_string))
- c
// base64.cpp
#include <iostream>
#include <windows.h>
#include "Base64.h"using namespace std;char *base64_encode(const char* data, int data_len)
{ //int data_len = strlen(data); int prepare = 0; int ret_len; int temp = 0; char *ret = NULL; char *f = NULL; int tmp = 0; char changed[4]; int i = 0; ret_len = data_len / 3; temp = data_len % 3; if (temp > 0) { ret_len += 1; } ret_len = ret_len*4 + 1; ret = (char *)malloc(ret_len); if ( ret == NULL) { printf("No enough memory.\n"); exit(0); } memset(ret, 0, ret_len); f = ret; while (tmp < data_len) { temp = 0; prepare = 0; memset(changed, '\0', 4); while (temp < 3) { //printf("tmp = %d\n", tmp); if (tmp >= data_len) { break; } prepare = ((prepare << 8) | (data[tmp] & 0xFF)); tmp++; temp++; } prepare = (prepare<<((3-temp)*8)); //printf("before for : temp = %d, prepare = %d\n", temp, prepare); for (i = 0; i < 4 ;i++ ) { if (temp < i) { changed[i] = 0x40; // 瀵瑰簲鐮佽〃涓殑 '=' } else { changed[i] = (prepare>>((3-i)*6)) & 0x3F; } *f = base[changed[i]]; //printf("%.2X", changed[i]); f++; } } *f = '\0'; return ret; } static char find_pos(char ch)
{ char *ptr = (char*)strrchr(base, ch);//the last position (the only) in base[] return (ptr - base);
} char *base64_decode(const char *data, int data_len)
{ int ret_len = (data_len / 4) * 3; int equal_count = 0; char *ret = NULL; char *f = NULL; int tmp = 0; int temp = 0;int prepare = 0; int i = 0; if (*(data + data_len - 1) == '=') { equal_count += 1; } if (*(data + data_len - 2) == '=') { equal_count += 1; } if (*(data + data_len - 3) == '=') {//seems impossible equal_count += 1; } switch (equal_count) { case 0: ret_len += 4;//3 + 1 [1 for NULL] break; case 1: ret_len += 4;//Ceil((6*3)/8)+1 break; case 2: ret_len += 3;//Ceil((6*2)/8)+1 break; case 3: ret_len += 2;//Ceil((6*1)/8)+1 break; } ret = (char *)malloc(ret_len); if (ret == NULL) { printf("No enough memory.\n"); exit(0); } memset(ret, 0, ret_len); f = ret; while (tmp < (data_len - equal_count)) { temp = 0; prepare = 0; while (temp < 4) { if (tmp >= (data_len - equal_count)) { break; } prepare = (prepare << 6) | (find_pos(data[tmp])); temp++; tmp++; } prepare = prepare << ((4-temp) * 6); for (i=0; i<3 ;i++ ) { if (i == temp) { break; } *f = (char)((prepare>>((2-i)*8)) & 0xFF); f++; } } *f = '\0'; return ret;
}int main() {char text[200];printf("请输入待加密字符:");int i=0;do{scanf("%s",&text[i]); i++;}while(getchar()!='\n');printf("%s\n", base64_encode(text, strlen(text)));return 0;
}
// base64.h#ifndef BASE64_H
#define BASE64_Hconst char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; char* base64_encode(const char* data, int data_len); char* base64_decode(const char* data, int data_len); #endif
参考博客:
一篇文章彻底弄懂Base64编码原理-CSDN博客
base64加密原理及python、C语言代码实现_base加密-CSDN博客