base16
#include<iostream>
using namespace std;
static const char BASE16_ENC_TAB[] = "0123456789ABCDEF";
static const char BASE16_DEC_TAB[] = {
-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 'A'~'F'
};
int Base16Encode(const unsigned char* in,int size,char* out)
{
for (int i = 0; i < size; i++)
{
//一个字节取出高4位和低4位 1000 0001 0000 1000
char high = in[i] >> 4; //移位丢弃低位 (0~15)
char low = in[i] & 0x0F; //0000 1111 去掉高位(0~15)
out[i * 2] = BASE16_ENC_TAB[high];//(0~15)映射到对应字符
out[i * 2 + 1] = BASE16_ENC_TAB[low];
}
//base16 转码后空间扩大一倍,4位转成一个字符,一个字节转成两个字符
return size * 2;
}
int Base16Decode(const string& in, unsigned char* out)
{
for (int i = 0; i < in.size(); i += 2)
{
unsigned char ch = (int)in[i];
unsigned char cl = (int)in[i + 1];
unsigned h = BASE16_DEC_TAB[ch];
unsigned l = BASE16_DEC_TAB[cl];
out[i / 2] = (int)(h << 4 | l);
}
return in.size() / 2;
}
int main(int argc, char* argv[])
{
cout << "test base16" << endl;
const unsigned char data[] = "测试base16";
int len = sizeof(data);
char out1[1024] = { 0 };
unsigned char out2[1024] = { 0 };
cout << data << endl;
int re = Base16Encode(data, len, out1);
cout << re << ":" << out1 << endl;
re = Base16Decode(out1, out2);
cout << re << ":" << out2 << endl;
return 0;
}
base64
base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*9=24),之后在6位的前面补两个0,形成8位一个字节的形式。如果剩下的字节不足3个字节,则用0填充,输出字符使用‘=’因此编码后输出的文本末尾可能会出现1或2个‘=’。
BIO接口
BIO包含了多种接口,用于控制在BIO_METHOD中的不同实现函数,包含6中filter型和8中source/sink型。应用场景:
BIO_new创建一个BIO对象
数据源:source/sink类型的BIO是数据源BIO_new(BIO_s_mem())
过滤:filter BIO就是把数据从一个BIO转换到另一个BIO或应用接口
BIO_new(BIO_f_base64());
BIO链:一个BIO链通常包括一个source BIO和一个或多个filter BIO
BIO_push(b64_bio,mem_bio);
写编码,读编码BIO_write BIO_read_ex
加码
int Base64Encode(const unsigned char* in, int len, char* out_base64)
{
if (!in || len <= 0 || !out_base64)
return 0;
//内存源 source
auto mem_bio = BIO_new(BIO_s_mem());
if (!mem_bio)return 0;
//base64 filter
auto b64_bio = BIO_new(BIO_f_base64());
if (!b64_bio)
{
BIO_free(mem_bio);
return 0;
}
//形成BIO链
//b64_mem
BIO_push(b64_bio, mem_bio);
//超过64字节不添加换行符(\n),编码的数据在一行中
// 默认结尾有换行符\n 超过64字节再添加\n
BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);
//写入到base64 filter 进行编码,结果会传递到链表的下一个节点,到mem中读取结果
// (从链表头部链表头部代表了整个链表)
//BIO_write编码 3字节->4字节 不足三字节 补充 0 和 =
//编码数据每64字节(不确定)会加\n换行符
int re = BIO_write(b64_bio, in, len);
if (re <= 0)
{
//清空整个链表节点
BIO_free_all(b64_bio);
return 0;
}
//刷新一下缓存,写入链表的mem
BIO_flush(b64_bio);
int outsize = 0;
//从链表源内存读取
BUF_MEM* p_data = 0;
BIO_get_mem_ptr(b64_bio, &p_data);
if (p_data)
{
memcpy(out_base64, p_data->data,p_data->length);
outsize = p_data->length;
}
BIO_free_all(b64_bio);
return outsize;
}
解码
int Base64Decode(const char* in, int len, unsigned char* out_data)
{
if (!in || len <= 0 || !out_data)
return 0;
//内存源(密文)
auto mem_bio = BIO_new_mem_buf(in, len);
if (!mem_bio) return 0;
//base64 过滤器
auto b64_bio = BIO_new(BIO_f_base64());
if (!b64_bio)
{
BIO_free(mem_bio);
return 0;
}
//形成BIO链
BIO_push(b64_bio, mem_bio);
//读取 解码
// 默认读取换行符做结束
//设置后编码中如果有\n会失败
BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);
//四字节转三字节
size_t size = 0;
BIO_read_ex(b64_bio, out_data, len, &size);
BIO_free(b64_bio);
return size;
}
整体代码
#include<iostream>
using namespace std;
#include<openssl/bio.h>
#include <openssl/evp.h>
#include<openssl/buffer.h>
int Base64Encode(const unsigned char* in, int len, char* out_base64)
{
......
}
int Base64Decode(const char* in, int len, unsigned char* out_data)
{
......
}
int main(int argc, char* argv[])
{
cout << "first openssl code" << endl;
unsigned char data[] = "测试base64数据";
int len = sizeof(data);
char out[1024] = { 0 };
unsigned char out2[1024] = { 0 };
cout << "source:" << data << endl;
int re = Base64Encode(data, len, out);
if (re > 0)
{
out[re] = '\0';
cout <<"encode:" << out << endl;
}
int res = Base64Decode(out, re, out2);
cout << "decode:" << out2 << endl;
getchar();
return 0;
}
base58
Base58 是一种用于编码数字的编码表,它主要用于加密货币地址中,如比特币等。与其他常见的编码表,如 Base64 不同,Base58 编码表不包含数字 0、大写字母 O、大写字母 I、小写字母 l 等易混淆的字符,可以避免在传输过程中的误解和错误。
Base58 编码表的原理是将数字转换为一组基于 58 个字符的字符串,每个字符对应于一个特定的值,从而实现对数字的编码。以下是 Base58 编码表的字符集:
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
其中,数字 0、大写字母 O、大写字母 I、小写字母 l 被排除在外,共计 58 个字符。
编码
//编码表
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
//编码函数:
std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
{
// Skip & count leading zeroes.
int zeroes = 0;
int length = 0;
while (pbegin != pend && *pbegin == 0) {
pbegin++;
zeroes++;
}
// Allocate enough space in big-endian base58 representation.
int size = (pend - pbegin) * 138 / 100 + 1; // log(256) / log(58), rounded up.
std::vector<unsigned char> b58(size);
while (pbegin != pend) {
int carry = *pbegin;
int i = 0;
// Apply "b58 = b58 * 256 + ch".
for (auto it = b58.rbegin();
(carry != 0 || i < length) && (it != b58.rend());
it++, i++) {
carry += 256 * (*it);
*it = carry % 58;
carry /= 58;
}
assert(carry == 0);
length = i;
pbegin++;
}
// Skip leading zeroes in base58 result.
std::vector<unsigned char>::iterator it = b58.begin() + (size - length);
while (it != b58.end() && *it == 0)
it++;
// Translate the result into a string.
std::string str;
str.reserve(zeroes + (b58.end() - it));
str.assign(zeroes, '1');
while (it != b58.end())
str += pszBase58[*(it++)];
return str;
}
解码
// 解码表需要自建
static const int8_t mapBase58[256] = {
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8,-1,-1,-1,-1,-1,-1,
-1, 9,10,11,12,13,14,15, 16,-1,17,18,19,20,21,-1,
22,23,24,25,26,27,28,29, 30,31,32,-1,-1,-1,-1,-1,
-1,33,34,35,36,37,38,39, 40,41,42,43,-1,44,45,46,
47,48,49,50,51,52,53,54, 55,56,57,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
};
//解码
bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch, int max_ret_len)
{
// Skip leading spaces.
while (*psz && IsSpace(*psz))
psz++;
// Skip and count leading '1's.
int zeroes = 0;
int length = 0;
while (*psz == '1') {
zeroes++;
if (zeroes > max_ret_len) return false;
psz++;
}
// Allocate enough space in big-endian base256 representation.
int size = strlen(psz) * 733 / 1000 + 1; // log(58) / log(256), rounded up.
std::vector<unsigned char> b256(size);
// Process the characters.
static_assert(sizeof(mapBase58) / sizeof(mapBase58[0]) == 256, "mapBase58.size() should be 256"); // guarantee not out of range
while (*psz && !IsSpace(*psz)) {
// Decode base58 character
int carry = mapBase58[(uint8_t)*psz];
if (carry == -1) // Invalid b58 character
return false;
int i = 0;
for (std::vector<unsigned char>::reverse_iterator it = b256.rbegin(); (carry != 0 || i < length) && (it != b256.rend()); ++it, ++i) {
carry += 58 * (*it);
*it = carry % 256;
carry /= 256;
}
assert(carry == 0);
length = i;
if (length + zeroes > max_ret_len) return false;
psz++;
}
// Skip trailing spaces.
while (IsSpace(*psz))
psz++;
if (*psz != 0)
return false;
// Skip leading zeroes in b256.
std::vector<unsigned char>::iterator it = b256.begin() + (size - length);
// Copy result into output vector.
vch.reserve(zeroes + (b256.end() - it));
vch.assign(zeroes, 0x00);
while (it != b256.end())
vch.push_back(*(it++));
return true;
}