Base64算法了解与实现

本文详细介绍了Base64编码原理,通过实例展示了编码过程,并探讨了编码表的使用和扩展,包括自定义编码位数和编码表的可能性。同时,提到了Base64编码在数据传递中的应用和其非加密特性。


一. 编码表


ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
(这里有65个字符, 其中64个用于编码, 最后一个用于补齐使用, 这里是'=')


例子: 字符串"abc"的编码过程
       明文:  a,         b         c        
十六进制:  0x61,      0x62,      0x63      (ASCII值)
   二进制:  0110 0001, 0110 0010, 01100011  (把这三个八位的二进制数拆分为四个六位的二进制数)
            (8 * 3 = 24 = 6 * 4; 刚刚好是6的整数倍)
            (24是8和6的最小公倍数) 
  二进制: 011000, 010110, 001001, 100011  (这4个值就是编码的索引)
  十进制: 24,     22,     9,      35
对应表值: Y,      W,      J,      j
    密文: YWJj

------------------------------------------------------------------------------------------
例子: 字符串"abcd"的编码过程
    明文: a,         b,         c,         d
十六进制: 0x61,      0x62,      0x63,      0x64
  二进制: 0110 0001, 0110 0010, 0110 0011, 0110 0100
  二进制: 011000, 010110, 001001, 100011, 011001, 000000
  十进制: 24,     22,     9,      35,     25,     0
对应表值: Y,      W,      J,      j,      Z,      A
    密文: YWJjZA==
(明文的长度要是3的倍数, 否则, 如果差1才是3的倍数, 就要在密文后加1个符号(这里是'=');
如果差2才是3的倍数, 就要在密文后加2个符号(这里是'=');
------------------------------------------------------------------------------------------

例子: 字符串"abcde"的编码过程
    明文: a,         b,         c,         d,         e
十六进制: 0x61,      0x62,      0x63,      0x64,      0x65
  二进制: 0110 0001, 0110 0010, 0110 0011, 0110 0100, 0110 0101
  二进制: 011000, 010110, 001001, 100011, 011001, 000110, 0101
  十进制: 24,     22,     9,      35,     25,     6,      5
对应表值: Y,      W,      J,      j,      Z,      G,      F
    密文: YWJjZGF=
(明文的长度要是3的倍数, 否则, 如果差1才是3的倍数, 就要在密文后加1个符号(这里是'=');
如果差2才是3的倍数, 就要在密文后加2个符号(这里是'=');

解码就是其逆过程了(有编码表就好办).


二. 实现

2.1 h文件

//////////////////////////////////////////////////////////////////////////////////////////
// FileName			: HYBase64.h
// Author			: Cay22
// CreateTime		: 2015-02-10 08:08:19
// Purpose			: Base64编解码的封装
// Note				: 1. 2015-02-10 08:08:19
//					:	编码表可以自己定义, 不用标准的.
// Log				: 1. 2015-02-10 08:08:19
//					:	对Base64编解码, 结合自己的使用习惯, 实现CHYBase64类.
//////////////////////////////////////////////////////////////////////////////////////////

#pragma once
#include <string>

class CHYBase64
{
public:
	CHYBase64();
	~CHYBase64();
	char* Base64Encode(const char *pData, long lDataLen, long& lTarLen);
	char* Base64Decode(const char *pData, long lDataLen, long& lTarLen);
private:
	int GetTableIndex(char chCode);
	void Release();

	char* m_pEncode;
	long m_lEncodeLen;

	char* m_pDecode;
	long m_lDecodeLen;

	char* m_chTable;
};

2.2 cpp文件

//////////////////////////////////////////////////////////////////////////////////////////
// FileName			: HYBase64.cpp
// Author			: Cay22
// CreateTime		: 2015-02-10 08:08:19
// Purpose			: Base64编解码的封装
// Note				: 1. 2015-02-10 08:08:19
//					:	编码表可以自己定义, 不用标准的.
// Log				: 1. 2015-02-10 08:08:19
//					:	对Base64编解码, 结合自己的使用习惯, 实现CHYBase64类.
//////////////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "HYBase64.h"

CHYBase64::CHYBase64()
	: m_chTable("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=")
	, m_pDecode(0)
	, m_lDecodeLen(0)
	, m_pEncode(0)
	, m_lEncodeLen(0)
{}

CHYBase64::~CHYBase64()
{
	Release();
}

void CHYBase64::Release()
{
	delete [] m_pEncode;
	m_pEncode = 0;
	m_lEncodeLen = 0;

	delete [] m_pDecode;
	m_pDecode = 0;
	m_lDecodeLen = 0;
}

// 编码
char* CHYBase64::Base64Encode(const char *pData, long lDataLen, long& lTarLen)
{
	int nMultiples = lDataLen / 3;
	int nMod = lDataLen % 3;

	m_lEncodeLen = nMultiples * 4;
	m_lEncodeLen += (0 == nMod) ? 0 : 4;

	delete [] m_pEncode;
	m_pEncode = new char[m_lEncodeLen + 1];
	memset(m_pEncode, 0, m_lEncodeLen + 1);

	int i = 0;
	lTarLen = 0;
	int nTableIndex = 0;
	for(i = 0; i < lDataLen; i += 3)
	{
		// 0
		nTableIndex = pData[i] >> 2;
		m_pEncode[lTarLen] = m_chTable[nTableIndex];
		++lTarLen;

		// 1
		if(i + 1 < lDataLen)
		{
			nTableIndex = ((pData[i] & 0x03) << 4) + (pData[i + 1] >> 4);
			m_pEncode[lTarLen] = m_chTable[nTableIndex];
			++lTarLen;
		}
		else
		{
			nTableIndex = ((pData[i + 1] & 0x03) << 4);
			m_pEncode[lTarLen] = m_chTable[nTableIndex];
			++lTarLen;

			m_pEncode[lTarLen] = m_chTable[64];
			++lTarLen;
			m_pEncode[lTarLen] = m_chTable[64];
			++lTarLen;
			break;
		}

		// 2
		if(i + 2 < lDataLen)
		{
			nTableIndex = ((pData[i + 1] & 0x0F) << 2) + (pData[i + 2] >> 6);
			m_pEncode[lTarLen] = m_chTable[nTableIndex];
			++lTarLen;
		}
		else
		{
			nTableIndex = ((pData[i + 1] & 0x0F) << 2);
			m_pEncode[lTarLen] = m_chTable[nTableIndex];
			++lTarLen;
			m_pEncode[lTarLen] = m_chTable[64];
			++lTarLen;
			break;
		}

		// 3
		nTableIndex = (pData[i + 2] & 0x3F);
		m_pEncode[lTarLen] = m_chTable[nTableIndex];
		++lTarLen;
	}
	return m_pEncode;
}

// 当编码表确定以后, 编码的索引也就确定了(这种关系其实是静态的)
// 而在上面的编码表除了静态以外, 还是有规律的.
// A-Z : 0-25
// a-z : 26-51
// 0-9 : 52-61
// +/= : 62, 63, 64
int CHYBase64::GetTableIndex(char chCode)
{
	if('+' == chCode)
	{
		return 62;
	}
	else if('/' == chCode)
	{
		return 63;
	}
	else if('=' == chCode)
	{
		return 64;
	}
	else if('9' >= chCode)
	{
		return chCode - '0' + 52;
	}
	else if('Z' >= chCode)
	{
		return chCode - 'A' + 0;
	}
	else if('z' >= chCode)
	{
		return chCode - 'a' + 26;
	}
	return 65;
}

// 解码
char* CHYBase64::Base64Decode(const char *pData, long lDataLen, long& lTarLen)
{
	lTarLen = 0;
	if(0 != (lDataLen % 4))
	{
		return 0;
	}

	int nMultiples = lDataLen / 4;
	m_lDecodeLen = nMultiples * 3 + 1;

	delete [] m_pDecode;
	m_pDecode = new char[m_lDecodeLen];
	memset(m_pDecode, 0, m_lDecodeLen);

	int i = 0;
	int nTableIndex[4];
	int nDataLenTemp = nMultiples * 3;
	for(i = 0; i < lDataLen; i += 4)
	{
		nTableIndex[0] = GetTableIndex(pData[i]);
		nTableIndex[1] = GetTableIndex(pData[i + 1]);
		nTableIndex[2] = GetTableIndex(pData[i + 2]);
		nTableIndex[3] = GetTableIndex(pData[i + 3]);

		// 0
		m_pDecode[lTarLen] = (nTableIndex[0] << 2) + (nTableIndex[1] >> 4);
		++lTarLen;

		// 1
		if(64 != nTableIndex[2])
		{
			m_pDecode[lTarLen] = ((nTableIndex[1] & 0x0F) << 4) + (nTableIndex[2] >> 2);
			++lTarLen;
		}

		// 2
		if(64 != nTableIndex[3])
		{
			m_pDecode[lTarLen] = ((nTableIndex[2] & 0x03) << 6) + nTableIndex[3];
			++lTarLen;
		}
	}
	return m_pDecode;
}

2.3 使用

	long lTarLen = 0;
	CHYBase64 hyBase64;
	std::string strEncodeData = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";
	hyBase64.Base64Encode("abc", 3, lTarLen);
	hyBase64.Base64Encode("abcd", 4, lTarLen);
	hyBase64.Base64Encode("abcde", 5, lTarLen);
	std::string strDecodeData = hyBase64.Base64Encode(strEncodeData.c_str(), strEncodeData.length(), lTarLen);
	std::string strData = hyBase64.Base64Decode(strDecodeData.c_str(), strDecodeData.length(), lTarLen);


参考: http://zh.wikipedia.org/wiki/Base64


三. 扩展


1. 这里的Base64编码就是基于编码表ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789的, 这里都是可视字符. 如果我换一个编码是不是也可以.
2. 这里的Base64编码每6位作为一个编码, 那么我每7位是不是也可以. 7位的话需要2的7次方这么多个编码(128个, 可视的字符没有128个, 我也可以用不可视的编码).

所以我这样实现
1. 随机产生一个位数, 作为编码位数, 例如6位就是64编码了, 7位就是128编码, ...
2. 根据位数, 产生对应长度的随机编码表, 同时产生编码与编码索引的映射.(这里编码表的内容不一定是可视字符).


四. 小结


1. 很明显的一点, Base64编码不是用来加密使用的.
2. 通过Base64编码, 你可以使用可视字符来传递数据.(个人认为这个很好用)
3. 扩展中有一点加密的用处(这里只能是作为简单加密使用), 但是前提是你不能被别人知道你的位数和编码表.


评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值