1.base16引申
#include <iostream>
typedef unsigned char uchar;
//用表来映射的话可以达到O1的复杂度
static const char base16EncodeTable[] = "0123456789ABCDEF";
//'0'-'9' == 48 - 57 'A' - 'F' == 65 -70
static const char base16DecTable[] =
{
-1,//0
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //1-10
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //11-20
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //21-30
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //31-40
-1,-1,-1,-1,-1,-1,-1, 0, 1, 2, //41-50
3, 4, 5, 6, 7, 8, 9,-1,-1,-1, //51-60
-1,-1,-1,-1,10,11,12,13,14,15, //61-70
};
int base16_encode(const uchar* in, int size, uchar* out)
{
for (int i = 0; i < size; ++i)
{
char h = in[i] >> 4; //拿到高位
char l = in[i] & 0x0F;//拿到低位
/*此时得到的h 和 l只是ascii表中的0~15的值,
需要经过base16EncodeTable表映射为可以打印的字符*/
out[i * 2] = base16EncodeTable[h];
out[i * 2 + 1] = base16EncodeTable[l];
}
return size * 2; //转码后空间扩大一倍,4位转成一个字符即,一个字节转成2个字节
}
int base16_decode(const uchar* in, int size, uchar* out)
{
for (int i = 0; i < size; i += 2)
{
uchar ch = in[i];//拿到高位型字符
uchar cl = in[i + 1]; //拿到低位型字符
uchar h = base16DecTable[ch];
uchar l = base16DecTable[cl];
out[i / 2] = h << 4 | l;
}
return size / 2;
}
int main()
{
std::cout << "base 16" << std::endl;
const uchar data[] = "测试base16";
int len = sizeof(data);
uchar out[1024] = { 0 };
int ret = base16_encode(data, len, out);
std::cout << "encode ret = " << ret << " data = " << out << std::endl;
memset((char*)data, 0, sizeof(data));
ret = base16_decode(out, ret, (uchar*)data);
std::cout << "decode ret = " << ret << " data = " << data << std::endl;
return 0;
}
2.base64
需注意 openssl的base64会默认加换行,我们需要手动调用API将其去掉,带_c的即为手动实现的c语言base64编解码
代码实现(看详细注释即可)
#include <iostream>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>
//typedef struct bio_st BIO;
//
//struct bio_st
//{
// BIO_METHOD* method; //BIO方法结构,是决定BIO类型和行为的重要参数,各种BIO的不同之处主要也正在于此项。
//
// long (*callback)(struct bio_st*, int, const char*, int, long, long); //BIO回调函数
// char* cb_arg; //回调函数的第一个参量
//
// int init; //初始化标志,初始化了为1,否则为0。比如文件BIO 中,通过BIO_set_fp
// //关联一个文件指针时,该标记则置1。
// int shutdown; //BIO开关标志,如果为BIO_CLOSE,则释放BIO时自动释放持有的资源,否则不自动释放持有资源
// int flags; //有些BIO 实现需要它来控制各个函数的行为。比如文件BIO 默认该值为BIO_FLAGS_UPLINK,
// //这时文件读操作调用UP_fread 函数而不是调用fread 函数。
// int retry_reason; //重试原因,主要用在socket 和ssl BIO 的异步阻塞。比如socketbio 中,遇到
// //WSAEWOULDBLOCK 错误时,openssl 告诉用户的操作需要重试
// int num; //该值因具体BIO 而异,比如socket BIO 中num 用来存放链接字。
// void* ptr; //ptr:指针,具体bio 有不同含义。比如文件BIO中它用来存放文件句柄;mem bio 中它用来存放
// //内存地址;connect bio 中它用来存放BIO_CONNECT 数据,acceptbio 中它
// //用来存放BIO_ACCEPT数据。
//
// struct bio_st* next_bio; //BIO链中下一个BIO 地址,BIO 数据可以从一个BIO 传送到另一个BIO。
// struct bio_st* prev_bio; //BIO链中上一个BIO 地址,
//
// int references; //引用计数
// unsigned long num_read; //已读出的数据长度
// unsigned long num_write; //已写入的数据长度
//
// CRYPTO_EX_DATA ex_data; //额外数据
//};
typedef unsigned char uchar;
int base64_encode(const uchar* in, int size, uchar* out)
{
int outsize = 0;
if (!in || size <= 0)
{
return false;
}
//内存源 source 封装了对内存操作的bio接口,包括了对内存的读写操作
BIO* mem_bio = BIO_new(BIO_s_mem());
//base64 filter 过滤器
BIO* base64_bio = BIO_new(BIO_f_base64()); //封装了对base64编码方法的bio,写的时候进行编码,读的时候进行解码
if (!base64_bio)
{
BIO_free(mem_bio);
return false;
}
//bio结构是一个链式结构,单个的bio是一个只有一个环节的bio的特例,在一个bio链中增加另一个bio
//使用的bio_push 删除的话是bio_pop
BIO_push(base64_bio, mem_bio); //mem_bio附加到base_64bio的bio上,并返回base_64bio 其实就是mem_bio前插一个base64_bio
//写入到base64 filter 进行编码,结果会传递到链表的下一个节点,即mem中读取数据
//链表头部代表整个链表 write进行编码 编码数据每64字节(不同平台不确定)加一个\n,最后一位也有换行符
BIO_set_flags(base64_bio, BIO_FLAGS_BASE64_NO_NL);//解决上述换行问题,会去掉所有换行符 不过解码也需要进行修改,因为最后一位也没有换行符了
int ret = BIO_write(base64_bio, in, size);
if (ret <= 0)
{
BIO_free_all(base64_bio);
return false;
}
//刷新缓存,写入链表的mem
BIO_flush(base64_bio);
BUF_MEM* p_data = nullptr;
//从链表源内存读取
BIO_get_mem_ptr(base64_bio, &p_data);
if (p_data)
{
memcpy(out, p_data->data, p_data->length);
outsize = p_data->length;
}
BIO_free_all(base64_bio);
return outsize;
}
int base64_decode(const uchar* in, int size, uchar* out)
{
size_t len = 0;
if (!in || size <= 0)
{
return false;
}
BIO* mem_bio = BIO_new_mem_buf(in, size);
BIO* base64_bio = BIO_new(BIO_f_base64());
if (!base64_bio)
{
BIO_free(mem_bio);
return false;
}
BIO_push(base64_bio, mem_bio);
BIO_set_flags(base64_bio, BIO_FLAGS_BASE64_NO_NL);//与解码进行对应,要保持一致,不然会失败 表示不会加换行
//读解码
BIO_read_ex(base64_bio, out,size, &len);
BIO_free_all(base64_bio);
return len;
}
static const char* base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char base64_detable[] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255,
255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255 };
int base64_encode_c(const uchar* in, int size, uchar* out)
{
uchar c1, c2, c3;
int i = 0, index = 0;
if (!in || !out)
{
return -1;
}
while (i < size)
{
c1 = in[i++]; //取本次循环的第一个字节
if (i == size) //如果本次循环第一个字节为总字节的最后一个字节
{
out[index++] = base64_table[c1 >> 2]; //取高位6个bit
out[index++] = base64_table[(c1 & 0x3)<<4];//取低位2个bit 后移拼接第二个字节的后4(0)
out[index++] = '='; //后面补 =
out[index++] = '=';
break;
}
c2 = in[i++];//取本次循环的第二个字节
if (i == size)//如果本次循环第二个字节为总字节的最后一个字节
{
out[index++] = base64_table[c1 >> 2];
out[index++] = base64_table[((c1 & 0x03) << 4) | ((c2&0xF0) >> 4)];
out[index++] = base64_table[((c2 & 0xF0) << 2)];
out[index++] = '=';
break;
}
c3 = in[i++];
out[index++] = base64_table[c1 >> 2]; //取高6位
out[index++] = base64_table[((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4)]; //第一个字节低2位左移4位 +第二个字节 高4位 右移4位
out[index++] = base64_table[((c2 & 0x0F) << 2)|((c3&0xC0)>>6)];//第二个字节低4位左移2位+ 第三个字节的高2位 右移6位
out[index++] = base64_table[c3 & 0x3F];//第三个字节的低6位
}
return index;
}
int base64_decode_c(const uchar* in, int size, uchar* out)
{
char c1, c2, c3, c4;
int i = 0, index = 0;
while (i < size)
{
do //读第一个字节并进行转换为真实数字
{
c1 = base64_detable[(int)in[i++]];
} while ((i < size) && (c1 == 255));
if (c1 == 255)
{
break;
}
do //读第二个字节并进行转换为真实数字
{
c2 = base64_detable[(int)in[i++]];
} while ((i < size) && (c2 == 255));
if (c2 == 255)
{
break;
}
out[index++] = (char)((c1 << 2) | (c2 & 0x30) >> 4);
do //读第三个字节并进行转换为真实数字
{
c3 = in[i++];
if (c3 == '=')
{
return index;
}
c3 = base64_detable[(int)c3];
} while ((i < size) && (c3 == 255));
if (c3 == 255)
{
break;
}
out[index++] = (char)((c2 & 0x0F) << 4 | (c3 & 0x3C) >> 2);
do //读第4个字节并进行转换为真实数字
{
c4 = in[i++];
if (c4 == '=')
{
return index;
}
c4 = base64_detable[(int)c4];
} while ((i < size) && (c4 == 255));
if (c4 == 255)
{
break;
}
out[index++] = (char)((c3 & 0x03) << 6 | (c4 & 0x3F));
}
return index;
}
int main()
{
std::cout << "oepnssl BIO base64" << std::endl;
unsigned char data[] = "测试base64001测试base64001测试base64001测试base64001测试base64001测试base64001测试base64001";
uchar out[4096] = { 0 };
int len = sizeof(data);
int ret = base64_encode(data, len, out);
if (ret > 0)
{
out[ret] = '\0';
std::cout <<"ret = " << ret<<" "<< out << std::endl;
}
memset((char*)data, 0, sizeof(data));
ret = base64_decode_c(out, ret, data);
if (ret > 0)
{
out[ret] = '\0';
std::cout << data << std::endl;
}
return 0;
}