Base64编码原理

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 z

1 B  18 S   35 j   52 0

2 C  19 T   36 k   53 1

3 D  20 U   37 l   54 2

4 E  21 V   38 m   55 3

5 F  22 W   39 n   56 4

6 G  23 X   40 o   57 5

7 H  24 Y   41 p   58 6

8 I  25 Z   42 q   59 7

9 J  26 a   43 r   60 8

10 K  27 b   44 s   61 9

11 L  28 c   45 t   62 +

12 M  29 d   46 u   63 /

13 N  30 e   47 v

14 O  31 f   48 w   

15 P  32 g   49 x

16 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 = string
        for 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_convert

    def fillIt(self, string, factor, item):
        """
        指定倍数填充指定字符
        string:原字符串
        factor:倍数
        item:填充字符
        """
        length = len(string)
        remainder = length % factor
        if remainder:
            times = factor - remainder
            string = string + times * item
        return string

    def 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 % bits
        if remain != 0:
            new_list.append(string[-remain:])
        return new_list


if __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_H

const 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博客

posted @ 2024-10-30 17:18  ihuahua1415  阅读(11)  评论(0编辑  收藏  举报
*/