吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2505|回复: 8
收起左侧

[C&C++ 原创] Base64编码原理&代码实现【附源码】

[复制链接]
kn0sky 发表于 2021-10-28 14:21
本帖最后由 kn0sky 于 2023-8-30 17:18 编辑

2021.12.1更新:使用位断进行base64编码操作,见文末

Base64编码原理

最近学某个东西的时候提到了Base64编码,可以用来数据加密,出于好奇,探究一下Base64到底是怎么一回事,便有了本文记录,如有问题,还请师傅们指出!

首先,Base64编码有什么用呢?

  1. 传输:用于将二进制数据编码成文本数据进行传输,比如图片的传输等...
  2. 加密:通过修改索引表进行编码,没有索引表就很难解码
  3. 还有啥用我就不知道了

其次,Base64编码是怎么一回事?

Base64一句话概括:就是把二进制数据按位切割形成索引,通过索引映射到索引表中的可见字符,用这个可见字符来代替二进制数据传输

因为一个字节的二进制数据有8位,数据范围是0~255,可见字符比这少多了,不能直接用这8位数据来索引可见字符

所以需要进行切割,拿出一部分位来进行当索引,根据ASCII码表,可见字符数量有26个大写字母+26个小写字母+10个数字+27个符号 = 89个:

ascii.jpg
直接用二进制位来当索引的话,需要取2的倍数个字符来当索引表,最大能取到2的6次方,也就是64个字符来作为索引表,索引需要6个二进制位(所以编码叫Base64,同理也有Base32,Base16等),通常默认使用的Base64索引表是这样的:

1964477-20200410171829355-65708843.png
于是需要把二进制数据以6位一划分,每6位获得一个索引号,根据索引号来转换可见字符,如下图所示,这里的二进制数据是字符串,每个字符都6位一分,得到索引号,使用索引号对应索引表得到一个可见字符,组成Base64编码

Center1.png
每3个字节,就能获得4个索引号,当不满3个字节时,有两种情况如下图所示,当索引号6位没有完整填充时,则会在索引号6位的空位填充0,如果索引位6位全是空的,则直接使用=字符(填充字符)代替索引获取字符

Center.png

图来自网络

原理部分介绍到此结束,接下来就是代码实现环节了

Base64编码代码实现

代码比较简单,所以也没啥注释,要注意一下,这里的关键点在于位运算(对位的切割与拼接)

测试环境:

  • Windows 10 21H1
  • Visual Studio 2019

Base64.h

#pragma once
#include <iostream>
#include <string>

class Base64calc
{
public:
        Base64calc();
        Base64calc(char * newIndexTable,char newEntrychar = '=');
        ~Base64calc();

public:
        void printIndexTable();
        char getIndex(char encodeChar);
        std::string encodeBase64(char* unencodeStr);
        std::string decodeBase64(char* encodeStr);
private:
        char indexTable[64];        // 索引表,需要64位,支持通过字符数组设定
        char entryChar;                // 空白填充符
};

Base64.cpp

#include "Base64.h"

Base64calc::Base64calc()
{
        // 生成默认索引表
        for (int i = 0; i < 64;i++ ) {
                if (i <= 25) {
                        indexTable[i] = 'A' + i;
                }
                else if (i > 25 && i <= 51) {
                        indexTable[i] = 'a' + i - 26;
                }
                else if(i>51 && i<=61){
                        indexTable[i] = '0' + i - 52;
                }
                else {
                        if (i == 62) {
                                indexTable[i] = '+';
                        }
                        if (i == 63) {
                                indexTable[i] = '/';
                        }
                }
        }
        entryChar = '=';
}

Base64calc::Base64calc(char* newIndexTable, char newEntrychar)
{
        memcpy(indexTable, newIndexTable, 64);
        entryChar = newEntrychar;
}

Base64calc::~Base64calc()
{

}

void Base64calc::printIndexTable()
{
        if (indexTable != NULL) {
                printf("IndexTable: ");
                for (int i = 0; i < 64; i++)
                {
                        std::cout << indexTable[i];
                }
                std::cout << std::endl;
        }
}

char Base64calc::getIndex(char encodeChar)
{
        for (char i = 0; i < 64; i++)
        {
                if (encodeChar == indexTable[i])return i;
        }
        return -1;
}

std::string Base64calc::encodeBase64(char* unencodeStr)
{
        std::string retStr;
        int strLength = strlen(unencodeStr);

        // 计算能完整转换的字节部分
        for (int i = 0; i < strLength - strLength % 3; i += 3) {
                char byte1 = unencodeStr[i];
                char byte2 = unencodeStr[i+1];
                char byte3 = unencodeStr[i+2];

                retStr += indexTable[byte1 >> 2];
                retStr += indexTable[byte2 >> 4 | (byte1 & 0x03) << 4];
                retStr += indexTable[byte3 >> 6 | (byte2 & 0x0F) << 2];
                retStr += indexTable[byte3 & 0x3F];
        }

        // 缺2字节时
        if (strLength % 3 == 1) {
                char byte = unencodeStr[strLength - 1];

                retStr += indexTable[byte >> 2];
                retStr += indexTable[(byte & 0x03) << 4];
                retStr += entryChar;
                retStr += entryChar;
        }

        // 缺1字节时
        if (strLength % 3 == 2) {
                char byte1 = unencodeStr[strLength - 2];
                char byte2 = unencodeStr[strLength - 1];

                retStr += indexTable[byte1 >> 2];
                retStr += indexTable[byte2 >> 4 | (byte1 & 0x03) << 4];
                retStr += indexTable[(byte2 & 0x0F) << 2];
                retStr += entryChar;
        }
        return retStr;
}

std::string Base64calc::decodeBase64(char* encodeStr)
{
        std::string retStr;
        int strLength = strlen(encodeStr);

        // 计算能完整转换的字节部分
        for (int i = 0; i < strLength; i += 4) {
                char byte1 = encodeStr[i];
                char byte2 = encodeStr[i + 1];
                char byte3 = encodeStr[i + 2];
                char byte4 = encodeStr[i + 3];

                retStr += (char)(getIndex(byte1) << 2) | (getIndex(byte2) >> 4);
                if (byte3 != '=')
                        retStr += (getIndex(byte2) << 4) | (getIndex(byte3) >> 2);
                if (byte4 != '=')
                        retStr += (getIndex(byte3) << 6) | (getIndex(byte4));
        }
        return retStr;
}

entry.cpp 测试代码

#include "Base64.h"
#include <iostream>
#include <string>

int main() {
        std::string str = "Hello selph";
        std::cout << "oriStr:" << str << std::endl;
        std::cout << std::endl;

        // 标准Base64索引表编码解码
        Base64calc test;
        test.printIndexTable();
        std::string encode = test.encodeBase64((char*)str.data());
        std::string decode = test.decodeBase64((char*)encode.data());
        std::cout << "encode:" << encode << std::endl;
        std::cout << "decode:" << decode << std::endl;

        std::cout << std::endl;

        // 自定义Base64索引表编码解码
        char* myIndexTable =(char*) "abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ<>";
        Base64calc testCostom(myIndexTable, 'a');
        testCostom.printIndexTable();
        std::string encode1 = testCostom.encodeBase64((char*)str.data());
        std::string decode1 = testCostom.decodeBase64((char*)encode1.data());
        std::cout << "encode:" << encode1 << std::endl;
        std::cout << "decode:" << decode1 << std::endl;
        return 0;
}

测试截图

image-20211028134638665.png

*****2021.12.1更新****

编码操作的简化

使用位断可简化base64编码操作,对于解码操作,已经够简单了不需要再简化了

Base64.h 新增内容

struct base64unit
{
        unsigned char b1 : 2;
        unsigned char a : 6;

        unsigned char c1 : 4;
        unsigned char b2 : 4;

        unsigned char d : 6;
        unsigned char c2 : 2;

        char index1;
        char index2;
        char index3;
        char index4;

        base64unit(const void* addr, int n) {
                memset(this, 0, sizeof(base64unit));
                memcpy((void*)this, addr, n);
                index1 = a;
                index2 = b1 << 4 | b2;
                index3 = c1 << 2 | c2;
                index4 = d;
        }
};

​Base64.cpp新增内容​

std::string Base64calc::encodeBase64(char* unencodeStr, int test)
{
        std::string retStrEx;
        int strLength = strlen(unencodeStr);

        // 计算能完整转换的字节部分
        for (int i = 0; strLength; i += 3, strLength -= 3) {
                base64unit tmp((const void*)&unencodeStr[i], strLength >= 3 ? 3 : strLength);

                retStrEx += indexTable[tmp.index1];
                retStrEx += indexTable[tmp.index2];
                if (strLength >= 3) {
                        retStrEx += indexTable[tmp.index3];
                        retStrEx += indexTable[tmp.index4];
                }
                else {
                        retStrEx += strLength == 1 ? entryChar : indexTable[tmp.index3];
                        retStrEx += entryChar;
                        break;
                }
        }
        return retStrEx;
}

由于计算结果是一样的,就不截图了

免费评分

参与人数 3吾爱币 +3 热心值 +3 收起 理由
imwaterp + 1 + 1 我很赞同!
forinv + 1 + 1 热心回复!
bylking + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

头像被屏蔽
tlf 发表于 2021-10-28 14:36
提示: 作者被禁止或删除 内容自动屏蔽
潇洒超人 发表于 2021-10-28 14:55
rapol0 发表于 2021-10-28 17:01
QingRemix 发表于 2021-10-28 17:14
谢谢分享学习了
liiyii00 发表于 2021-10-28 18:30
学习一下 谢谢
2513002960 发表于 2021-10-31 21:47
厉害了,之前只用,从来没去了解它的原理
Ishin 发表于 2021-11-1 00:21
[C++] 纯文本查看 复制代码
retStr += indexTable[byte1 >> 2];//原先是8bit,现在只要取前6bit作为索引。
        retStr += indexTable[byte2 >> 4 | (byte1 & 0x03) << 4];// 这里到底是什么意思?byte1 & 00000011 ?
        retStr += indexTable[byte3 >> 6 | (byte2 & 0x0F) << 2];// 这里到底是什么意思?byte2 & 00000111 ?
        retStr += indexTable[byte3 & 0x3F];

上面这一段我实在看不懂到底是什么意思?
为什么byte1要和00000011 进行位与运算?不是应该直接左移4位就可以拿到需要的4位的值了吗?,然后直接和byte2的前2为进行按位或运算就好了?
我苦思而万万不能解?
求解释,谢谢~~!!
 楼主| kn0sky 发表于 2021-11-2 18:24
Ishin 发表于 2021-11-1 00:21
[mw_shl_code=cpp,true]retStr += indexTable;//原先是8bit,现在只要取前6bit作为索引。
        retStr + ...

首先要知道:
第一个索引值是byte1的前6位
第二个索引值是byte1的最后2位,和byte2的前4位
因为只需要用到byte1的最后两位,所以与运算一下0x11,这样就能将其他位置零,单独取出2位来
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 11:58

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表