base64/base32/base16编码方案详解

编码方案

  • Base encoding 编码规格书 RFC3548
  • 把二进制数据转化为可打印字符集数据称为base encoding
  • 编码后的数据方便用于存储和网络传输

Base64

  • 把8bit数据用6bit进行编码,即把0 ~ 2 8 2^{8} 的字符集合A映射到0 ~ 2 6 2^6 的字符集合B内
  • 字符集合B的选择有2种:
    • 标准集合:A~Z && a ~ z && 0 ~ 9 && + /
    • 用于path和url的编码:A~Z && a ~ z && 0 ~ 9 && - _

      由于path和url中不能出现’/’,因此用 ‘-’ , ‘_’ 来替代’+’ , ‘/’html

编码流程

  • 按照bit流进行编码,24(6和8的最小公倍数)bit为一组.
    在这里插入图片描述
  • 有如下几种填充状况:
    • input的长度(单位bit)是24的倍数,正常编码不须要填充
    • input 多8bit,须要填充16bit 0,使用了填充的前4bit的0后,剩余的12bit使用2个’='表示
    • input 多16bit,须要填充8bit 0,使用填充的前2bit的0后,剩余的6bit使用1个‘=’表示
  • 通过Base64编码后的数据膨胀约33.3%
  • encoding
const std::string  Base64::base64_chars_{"ABCDEFGHIJKLMNOPQRSTUVWXYZ""abcdefghijklmnopqrstuvwxyz0123456789+/"};
std::string Base64::encode(const std::string &to_encode_string){
    if(to_encode_string.empty()){
        return {};
    }
    std::string ret{""};
    int i = 0,j = 0;
    const unsigned char * bytes_to_encode = reinterpret_cast<const unsigned char*>(to_encode_string.c_str());
    unsigned int in_len = to_encode_string.size();
    unsigned char char_array_3[3];
    unsigned char char_array_4[4];

    while (in_len--) {
        char_array_3[i++] = *(bytes_to_encode++);
        if (i == 3) {
	        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
	        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
	        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
	        char_array_4[3] = char_array_3[2] & 0x3f;
	
	        for(i = 0; (i <4) ; i++)
	            ret += base64_chars_[char_array_4[i]];
	        i = 0;
        }
    }
    //i为len%3
    if (i)
    {
        for(j = i; j < 3; j++)
	        char_array_3[j] = '\0';
	
	        char_array_4[0] = ( char_array_3[0] & 0xfc) >> 2;
	        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
	        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
	
	        for (j = 0; (j < i + 1); j++)
	        	ret += base64_chars_[char_array_4[j]];
			//空余几个
	        while((i++ < 3))
	        	ret += '=';
    }
    return ret;
}
  • decoding
if(encoded_string.empty()){
        return {};
    }
    int in_len = encoded_string.size();
    int i = 0;
    int j = 0;
    int in_ = 0;
    unsigned char char_array_4[4], char_array_3[3];
    std::string ret{""};

    while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
        char_array_4[i++] = encoded_string[in_]; in_++;
        if (i ==4) {
	        for (i = 0; i <4; i++)
	            char_array_4[i] = base64_chars_.find(char_array_4[i]);
	
	        char_array_3[0] = ( char_array_4[0] << 2       ) + ((char_array_4[1] & 0x30) >> 4);
	        char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
	        char_array_3[2] = ((char_array_4[2] & 0x3) << 6) +   char_array_4[3];
	
	        for (i = 0; (i < 3); i++)
	            ret += char_array_3[i];
	        i = 0;
        }
    }

    if (i) {
        for (j = 0; j < i; j++)
        	char_array_4[j] = base64_chars_.find(char_array_4[j]);

        char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
        char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);

        for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
    }
    return ret;

Base32

  • 把0 ~ 2 8 2^{8} 数据集合A映射到 0 ~ 2 5 2^{5} 的数据集合B上
  • 数据集合B:A~Z && 2 ~ 7
    在这里插入图片描述
  • 有如下几种填充状况:
    • input的长度(bit)是40的整数倍,不须要填充
    • 多出来8bit,须要填充32bit的0,使用了填充的前2个bit,剩下的30bit须要用6个‘=’替代
    • 多出来16bit,须要填充24bit的0,使用了填充的前4个bit,剩下的20bit须要用4个‘=’替代
    • 多出来24bit,须要填充16bit的0,使用了填充的前1个bit,剩下的15bit须要用3个‘=’替代
    • 多出来32bit,须要填充8bit的0,使用了填充的前3个bit,剩下的5bit须要用1个‘=’替代

Base16

  • 把0 ~ 2 8 2^{8} 数据集合A映射到 0 ~ 2 4 2^{4} 的数据集合B上
  • 数据集合B:0~9 && A ~F
  • 也就是HEX编码
  • 每一个字节都编码为2个字节
  • 不存在填充状况
class Base16{
	const static std::string base16_chars;
	static bool check(unsigned char it){
		if(isdigit(it) || (it >= 'A' && it <= 'F')) return true;
		return false;
	}	
	static unsigned char toDecode(unsigned char i){
		return base16_chars[i];
	}
	static unsigned char fromDecode(unsigned char it){
		if(it > 'F' || it < '0') throw InvalidParam("invalid input");
		if(it >= 'A') return it -'A' + 10;
		if(it <= '9') return it - '0';
		throw InvalidParam("invalid input");
	}
public:
	class InvalidParam:public std::exception{
			std::string msg_;
		public:
			InvalidParam(const std::string& m):msg_(m){}
			const char* what() const noexcept override{
				return msg_.c_str();
			}

	};
	static std::string encode(const std::string& in);
	static std::string decode(const std::string& in);
};
const std::string Base16::base16_chars{"0123456789ABCDEF"};

std::string Base16::encode(const std::string& in){
	int size = in.size();
	std::string res(2*size,0);
	int j = 0;
	for(int i = 0;i < size;++i){
		unsigned char it = in[i];
		res[j++] = toDecode(it>>4);
		res[j++] = toDecode(it&0x0f);
	}
	return res;
}
std::string Base16::decode(const std::string&in){
	int size = in.size();
	if(size %2 != 0) throw InvalidParam("invalid input");;
	std::string res(size/2,0);
	int j = 0;
	for(int i = 0;i < size;++i){
		if(!check(in[i]) || !check(in[i+1])){
			throw InvalidParam("invalid input");
		}	
		res[j++] = fromDecode(in[i])*16+fromDecode(in[i+1]);
		i++;
	}
	return res;
}