Base64编解码及C++代码实现
Base64是一种二进制到文本的编码方式。如果要更具体一点的话,可以认为它是一种将byte数组编码为字符串的方法,而且编码出的字符串只包含ASCII基础字符。例如字符串 mickey0380 对应的Base64为 bWlja2V5MDM4MA==。其中那个 = 比较特殊,是填充符。值得注意的是Base64不是加密算法,其仅仅是一种编码方式,算法也是公开的,所以不能依赖它进行加密。
1.Base64是什么?
Base64是一种二进制到文本的编码方式。如果要更具体一点的话,可以认为它是一种将 byte
数组编码为字符串的方法,而且编码出的字符串只包含ASCII基础字符。
例如字符串 mickey0380 对应的Base64为 bWlja2V5MDM4MA== 。其中那个 = 比较特殊,是填充符。
值得注意的是Base64不是加密算法,其仅仅是一种编码方式,算法也是公开的,所以不能依赖它进行加密。
2.为什么叫Base64?
因为它是基于(Base)64个字符的一种编码方式。使用其编码后的文本只包含64个ASCII码字符(偶尔加一个填充字符=
),如下所示:
Base64使用到的64个字符:
-
A-Z
26个 -
a-z
26个 -
0-9
10个 -
+
1个 -
/
1个
下表是Base64码表,可以看到从0到63的每个数字都对应一个上面的一个字符。
Index | Binary | Char | Index | Binary | Char | Index | Binary | Char | Index | Binary | Char |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 000000 | A | 16 | 010000 | Q | 32 | 100000 | g | 48 | 110000 | w |
1 | 000001 | B | 17 | 010001 | R | 33 | 100001 | h | 49 | 110001 | x |
2 | 000010 | C | 18 | 010010 | S | 34 | 100010 | i | 50 | 110010 | y |
3 | 000011 | D | 19 | 010011 | T | 35 | 100011 | j | 51 | 110011 | z |
4 | 000100 | E | 20 | 010100 | U | 36 | 100100 | k | 52 | 110100 | 0 |
5 | 000101 | F | 21 | 010101 | V | 37 | 100101 | l | 53 | 110101 | 1 |
6 | 000110 | G | 22 | 010110 | W | 38 | 100110 | m | 54 | 110110 | 2 |
7 | 000111 | H | 23 | 010111 | X | 39 | 100111 | n | 55 | 110111 | 3 |
8 | 001000 | I | 24 | 011000 | Y | 40 | 101000 | o | 56 | 111000 | 4 |
9 | 001001 | J | 25 | 011001 | Z | 41 | 101001 | p | 57 | 111001 | 5 |
10 | 001010 | K | 26 | 011010 | a | 42 | 101010 | q | 58 | 111010 | 6 |
11 | 001011 | L | 27 | 011011 | b | 43 | 101011 | r | 59 | 111011 | 7 |
12 | 001100 | M | 28 | 011100 | c | 44 | 101100 | s | 60 | 111100 | 8 |
13 | 001101 | N | 29 | 011101 | d | 45 | 101101 | t | 61 | 111101 | 9 |
14 | 001110 | O | 30 | 011110 | e | 46 | 101110 | u | 62 | 111110 | + |
15 | 001111 | P | 31 | 011111 | f | 47 | 101111 | v | 63 | 111111 | - |
Padding | = |
3.Base64编码步骤
-
将原始数据每三个字节作为一组,每个字节是8个bit,所以一共是 24 个 bit
-
将 24 个 bit 分为四组,每组 6 个 bit
-
在每组前面加补 00,将其补全成四组8个bit 到此步,原生数据的3个字节已经变成4个字节了,增大了将近
30%
-
根据Base64码表得到扩展后每个字节的对应符号(见上表)
下图是维基百科上面的一个例子,假如我们的原文为Man
,那么下表演示了如何按照上面的步骤将其编码为Base64字符串
可以发现Man
对应的Base64为TWFu
.现在大家应该明白为什么只有64个字符了吧?因为算法将将8bit分割成6bit了,而6bit的取值范围为0~63
。
如果要编码的字节数不能被3整除,最后会多出1个或2个字节,那么可以使用下面的方法进行处理:先使用0字节值在末尾补足,使其能够被3整除,然后再进行base64的编码。在编码后的base64文本后加上一个或两个'='号,代表补足的字节数。也就是说,当最后剩余一个八位字节(一个byte)时,最后一个6位的base64字节块有四位是0值,最后附加上两个等号;如果最后剩余两个八位字节(2个byte)时,最后一个6位的base字节块有两位是0值,最后附加一个等号。 参考下图:
4.Base64URL
Base64 能够将二进制流很好的转换为一串可打印字符流,然而编码后的 =、/、+ 等字符不利于 url 中的查询参数、数据库保存时的转义等,所以在实际应用的场景中又产生了一种几乎等价的编码方案 Base64URL。
Base64URL 的基本规则与 Base64 一模一样,差别在于最后两个特殊字符 +、/ 改成使用 -、_,同时也去除末尾额外添加的 = 字符即完成 Base64URL 的编码
4.1 Base64URL编码流程
1、明文使用BASE64进行加密
2、在BASE64的基础上进行一下的编码:
2.1)去除尾部的"="
2.2)把"+"替换成"-"
2.3)把"/"替换成"_"
4.2 Base64URL解码流程
1、把BASE64URL的编码做如下解码:
1)把"-"替换成"+"
2)把"_"替换成"/"
3)(计算BASE64URL编码长度)%4
3.1)结果为0,不做处理
3.2)结果为2,字符串添加"=="
3.3)结果为3,字符串添加"="
2、使用BASE64解码密文,得到原始的明文
5. Base64及Base64URL编解码C++代码实现
头文件 Base64Util.h
#pragma once
#if defined(_WIN32) || defined(_WIN64)
#ifndef WINAPI
#define WINAPI __stdcall
#endif
#else
#define WINAPI
#endif
//#ifdef __cplusplus
//extern "C" {
//#endif
/*********************************************************************************
功 能: 对数据块进行Base64编码
参 数: pInput - [in]编码前数据块
nLength - [in]输入数据块(pInput)长度
pOutput - [out]Base64编码后数据块,大小为输入数据的4/3倍,
输出数据块pInput 和输入数据块pOutput 起始地址可以相同
nOutBufSize - [in]存放编码后数据(pOutput)的缓冲区大小
返 回: 0 - 用于存放编码后数据的缓冲区不够,编码失败。
大于0 - 编码后数据长度,值为(inputLen+2)/3*4
*********************************************************************************/
int WINAPI Base64Encode(unsigned char *pInput, int nLength, char *pOutput, int nOutBufSize);
/*********************************************************************************
功 能:对输入的Base64编码数据块进行Base64解码
参 数:pInput - [in]Base64编码数据块
nLength - [in]Base64编码数据块长度
pOutput - [out]Base64解码后的数据块
输出数据块pInput和输入数据块pOutput起始地址可以相同
返 回: 0 - 无效数据,解码失败
大于0 - Base64解码后数据长度
*********************************************************************************/
int WINAPI Base64Decode(const char *pInput, int nLength, unsigned char* pOutput);
/*********************************************************************************
功 能: 对数据块进行Base64URL编码
参 数: pInput - [in]编码前数据块
nLength - [in]输入数据块(pInput)长度
pOutput - [out]Base64URL编码后数据块,大小为输入数据的4/3倍,
输出数据块pInput 和输入数据块pOutput 起始地址可以相同
nOutBufSize - [in]存放编码后数据(pOutput)的缓冲区大小
返 回: 0 - 用于存放编码后数据的缓冲区不够,编码失败。
大于0 - 编码后数据长度,值为(inputLen+2)/3*4
*********************************************************************************/
int WINAPI Base64URLEncode(unsigned char *pInput, int nLength, char *pOutput, int nOutBufSize);
/*********************************************************************************
功 能:对输入的Base64URL编码数据块进行base64URL解码
参 数:pInput - [in]Base64URL编码数据块
nLength - [in]Base64URL编码数据块长度
pOutput - [out]Base64URL解码后的数据块
输出数据块pInput和输入数据块pOutput起始地址可以相同
返 回: 0 - 无效数据,解码失败
大于0 - Base64URL解码后数据长度
*********************************************************************************/
int WINAPI Base64URLDecode(const char *pInput, int nLength, unsigned char* pOutput);
//#ifdef __cplusplus
//}
//#endif
源文件 Base64Util.cpp
#include "stdafx.h"
#include <malloc.h>
#include "Base64Util.h"
/* 0-63的数转化为可见字符查表 */
char g_b64EncTable[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
unsigned char g_b64DecTable[256] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
int WINAPI Base64Encode(unsigned char *pInput, int nLength, char *pOutput, int nOutBufSize)
{
int nModulus = nLength%3;
int nOutLen = (nLength+2)/3*4; /* 编码后数据是4的整数倍 */
int nDatalen = nLength-nModulus;
register int i, j;
register int x, y, z;
/* 缓冲区大小校验 */
if(nOutBufSize < nOutLen)
{
return 0;
}
for (i=0, j=0; i<nDatalen; i+=3,j+=4)
{
x = pInput[i];
y = pInput[i+1];
z = pInput[i+2];
pOutput[j] = g_b64EncTable[x>>2];
pOutput[j+1] = g_b64EncTable[((x&3)<<4)|(y>>4)];
pOutput[j+2] = g_b64EncTable[((y&15)<<2)|(z>>6)];
pOutput[j+3] = g_b64EncTable[z&63];
}
i = nDatalen;
j = (nDatalen/3)*4;
if (nModulus == 1)
{
x = pInput[i];
pOutput[j] = g_b64EncTable[x>>2];
pOutput[j+1] = g_b64EncTable[(x&3)<<4];
pOutput[j+2] = '=';
pOutput[j+3] = '=';
}
else if (nModulus == 2)
{
x = pInput[i];
y = pInput[i+1];
pOutput[j] = g_b64EncTable[x>>2];
pOutput[j+1] = g_b64EncTable[((x&3)<<4)|(y>>4)];
pOutput[j+2] = g_b64EncTable[(y&15)<<2];
pOutput[j+3] = '=';
}
return nOutLen;
}
int WINAPI Base64Decode(const char *pInput, int nLength, unsigned char* pOutput)
{
register int i,j;
register int x, y, z, k;
int nOutLen;
int nPadBytes;
int nDataLen;
if(nLength%4 != 0)
{
return 0;
}
/* 根据'='的个数判断编码前数据的长度 */
if(pInput[nLength-2] == '=')
{
nPadBytes = 2;
}
else if(pInput[nLength-1] == '=')
{
nPadBytes = 1;
}
else
{
nPadBytes = 0;
}
nOutLen = nLength/4*3 - nPadBytes;
nDataLen = (nLength-nPadBytes)/4*3;
/* 开始解码 */
for(i=0, j=0; i<nDataLen; i+=3, j+=4)
{
x = g_b64DecTable[pInput[j]];
y = g_b64DecTable[pInput[j+1]];
z = g_b64DecTable[pInput[j+2]];
k = g_b64DecTable[pInput[j+3]];
if (x==0xff || y==0xff || z==0xff || k==0xff)
{
return 0;
}
pOutput[i] = (x<<2)|(y>>4);
pOutput[i+1] = ((y&15)<<4)|(z>>2);
pOutput[i+2] = ((z&3)<<6)|k;
}
i = nDataLen;
if (nPadBytes == 1)
{
x = g_b64DecTable[pInput[j]];
y = g_b64DecTable[pInput[j+1]];
z = g_b64DecTable[pInput[j+2]];
pOutput[i] = (x<<2)|(y>>4);
pOutput[i+1] = ((y&15)<<4)|(z>>2);
}
else if (nPadBytes == 2)
{
x = g_b64DecTable[pInput[j]];
y = g_b64DecTable[pInput[j+1]];
pOutput[i] = (x<<2)|(y>>4);
}
return nOutLen;
}
/*
Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法
BASE64URL编码的流程:
1、明文使用BASE64进行加密
2、在BASE64的基础上进行一下的编码:
2.1)去除尾部的"="
2.2)把"+"替换成"-"
2.3)把"/"替换成"_"
BASE64URL解码的流程:
1、把BASE64URL的编码做如下解码:
1)把"-"替换成"+"
2)把"_"替换成"/"
3)(计算BASE64URL编码长度)%4
3.1)结果为0,不做处理
3.2)结果为2,字符串添加"=="
3.3)结果为3,字符串添加"="
2、使用BASE64解码密文,得到原始的明文
*/
int WINAPI Base64URLEncode(unsigned char *pInput, int nLength, char *pOutput, int nOutBufSize)
{
//1.明文使用BASE64进行加密
int nOutLen = Base64Encode(pInput,nLength, pOutput,nOutBufSize);
if (nOutLen <= 0)
{
return nOutLen;
}
//2.在BASE64的基础上进行一下的编码
//2.1 去除尾部的"="
if ('=' == pOutput[nOutLen - 2])
{
pOutput[nOutLen - 2] = '\0';
nOutLen = nOutLen - 2;
}
else if ('=' == pOutput[nOutLen - 1])
{
pOutput[nOutLen - 1] = '\0';
nOutLen = nOutLen - 1;
}
// 2.2)把"+"替换成"-"
// 2.3)把"/"替换成"_"
for (int i = 0; i<nOutLen; i++)
{
if ('+' == pOutput[i])
pOutput[i] = '-';
else if ('/' == pOutput[i])
pOutput[i] = '_';
}
return nOutLen;
}
int WINAPI Base64URLDecode(const char *pInput, int nLength, unsigned char* pOutput)
{
int iTmpLen = nLength;
char* pTmpBuffer = (char*)malloc((nLength + 10)*sizeof(char));
memcpy(pTmpBuffer, pInput, nLength);
//1、把BASE64URL的编码做如下解码
// 1)把"-"替换成"+".
// 2)把"_"替换成"/" .
for (int i = 0; i<nLength; i++)
{
if ('-' == pTmpBuffer[i])
pTmpBuffer[i] = '+';
else if ('_' == pTmpBuffer[i])
pTmpBuffer[i] = '/';
}
//3)(计算BASE64URL编码长度)%4
// 3.1)结果为0,不做处理
// 3.2)结果为2,字符串添加"=="
// 3.3)结果为3,字符串添加"="
int nModulus = nLength % 4;
if (2 == nModulus)
{
pTmpBuffer[nLength] = '=';
pTmpBuffer[nLength+1] = '=';
iTmpLen = nLength + 2;
}
else if (3 == nModulus)
{
pTmpBuffer[nLength] = '=';
iTmpLen = nLength + 1;
}
// 使用BASE64解码密文,得到原始的明文
int nOutLen = Base64Decode(pTmpBuffer, iTmpLen, pOutput);
free(pTmpBuffer);
return nOutLen;
}
6. C++测试工程源代码
编译器版本:VS2015
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)