Base 64概述和應(yīng)用場(chǎng)景
概述
Base64就是將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換為字符串的一種算法。
應(yīng)用場(chǎng)景
- 郵件編碼
-
xml
或則json
存儲(chǔ)二進(jìn)制內(nèi)容 - 網(wǎng)頁(yè)傳遞數(shù)據(jù)
URL
- 數(shù)據(jù)庫(kù)中以文本形式存放二進(jìn)制數(shù)據(jù)
- 可打印的比特幣錢(qián)包地址
base58Check
(hash校驗(yàn)) - 網(wǎng)頁(yè)上可以將圖片直接使用
Base64
表達(dá) - 公私密鑰的文本文件
Base16(16進(jìn)制)
Base16
是4位
, 一個(gè)Unicode
字符編碼需要8位
,那就需要將一個(gè)字符分解成2部分。編碼字節(jié)的值,對(duì)應(yīng)Base64的值如下對(duì)照表:
字節(jié)值 | Base64編碼 |
---|---|
0 | 0 |
1 | 1 |
2 | 2 |
3 | 3 |
4 | 4 |
5 | 5 |
6 | 6 |
7 | 7 |
8 | 8 |
9 | 9 |
10 | A |
11 | B |
12 | C |
13 | D |
14 | E |
15 | F |
從零開(kāi)始實(shí)現(xiàn)Base16編解碼
代碼如下:
#include <iostream>
using namespace std;
static const char BASE16_ENC_TAB[] = "0123456789ABCDEF";
// '0'~'9' => 48~57, 'A'~'E' => 65~70
static const char BASE16_DEC_TAB[128] =
{
-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++)
{
//1 一個(gè)字節(jié)取出高4位和低4位
char h = (in[i] >> 4) ; // 以為
char l = (in[i] & 0x0f); // 0000 1111 //去掉高4位
// 0~15映射到對(duì)應(yīng)的字符
out[i * 2] = BASE16_ENC_TAB[h];
out[i * 2 + 1] = BASE16_ENC_TAB[l];
}
// base 16轉(zhuǎn)碼后空間擴(kuò)大一倍 4位轉(zhuǎn)成一個(gè)字符 1字節(jié)轉(zhuǎn)成兩個(gè)字符
return size * 2;
}
/**
* 將Base16字符轉(zhuǎn)換成常規(guī)字符串
*/
int Base16Decode(const string &in, unsigned char* out)
{
// 將兩個(gè)字符拼成一個(gè)字節(jié)
for (int i = 0; i < in.size(); i+=2)
{
unsigned char ch = in[i]; //高位轉(zhuǎn)換的字符
unsigned char lh = in[i + 1]; // 低位轉(zhuǎn)換的字符
// 上面拿到的還是個(gè)字符, 要轉(zhuǎn)換成原始的數(shù)據(jù)
unsigned char h = BASE16_DEC_TAB[ch];
unsigned char l = BASE16_DEC_TAB[lh];
//out[i/2] = (h <<4) + l;
out[i / 2] = h << 4 | l;
}
return in.size() / 2;
}
int main()
{
cout << "Test Base16" << endl;
const unsigned char data[] = "測(cè)試Base16";
int len = sizeof(data);
char out1[1024] = { 0 };
int res = Base16Encode(data, len, out1);
cout << res << ":" << out1 << endl;
string code(out1);
unsigned char out2[1024] = { 0 };
res = Base16Decode(code, out2);
cout << res << ":" << out2 << endl;
return 0;
}
Base64(64進(jìn)制)
首先查看Base64
的值碼對(duì)應(yīng)表
字節(jié)值 | Base64編碼 | 字節(jié)值 | Base64編碼 | 字節(jié)值 | Base64編碼 | 字節(jié)值 | Base64編碼 | |||
---|---|---|---|---|---|---|---|---|---|---|
0 | A | 16 | Q | 32 | g | 48 | w | |||
1 | B | 17 | R | 33 | h | 49 | x | |||
2 | C | 18 | S | 34 | i | 50 | y | |||
3 | D | 19 | T | 35 | j | 51 | z | |||
4 | E | 20 | U | 36 | k | 52 | 0 | |||
5 | F | 21 | V | 37 | l | 53 | 1 | |||
6 | G | 22 | W | 38 | m | 54 | 2 | |||
7 | H | 23 | X | 39 | n | 55 | 3 | |||
8 | I | 24 | Y | 40 | o | 56 | 4 | |||
9 | J | 25 | Z | 41 | p | 57 | 5 | |||
10 | K | 26 | a | 42 | q | 58 | 6 | |||
11 | L | 27 | b | 43 | t | 59 | 7 | |||
12 | M | 28 | c | 44 | s | 60 | 8 | |||
13 | N | 29 | d | 45 | t | 61 | 9 | |||
14 | O | 30 | e | 46 | u | 62 | + | |||
15 | P | 31 | f | 47 | v | 63 | / |
Base64
編碼要求把3個(gè)8位字節(jié)(3*8=24), 之后在6位的前面補(bǔ)兩個(gè)0, 形成8位一個(gè)字節(jié)的形式。如果剩下的字符不足3個(gè)字節(jié), 則用0填充,輸出字符使用=
,因此編碼后輸出的文本末尾可能會(huì)出現(xiàn)1或2個(gè)=
。
Open SSL BIO接口
-
BIO
包含了多種接口,用于控制在BIO_METHOD
中不同實(shí)現(xiàn)函數(shù), 包括6種filter型和8種source/sink型應(yīng)用場(chǎng)景。 - BIO_new創(chuàng)建一個(gè)BIO對(duì)象.
- 數(shù)據(jù)源:
source/sink
類(lèi)型的BIO
是數(shù)據(jù)源BIO_new(BIO_s_mem()),生存內(nèi)存是數(shù)據(jù)源對(duì)象 - 過(guò)濾:filter BIO就是把數(shù)據(jù)從一個(gè)BIO轉(zhuǎn)換到另外一個(gè)BIO或應(yīng)用接口 BIO_new(BIO_f_base64())
- BIO鏈:一個(gè)BIO鏈通常包括一個(gè)source BIO和一個(gè)或多個(gè)filter BIO BIO_push(b64_bio, mem_bio);
- 寫(xiě)編碼, 讀解碼 BIO_write BIO_read_ex
Open SSL BIO
實(shí)現(xiàn)Base64編解碼
#include <iostream>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>
using namespace std;
int Base64Encode(const unsigned char* in, int len, char* out_base64)
{
if (!in || len < 0 || !out_base64)
{
return 0;
}
//創(chuàng)建內(nèi)存源
auto mem_bio = BIO_new(BIO_s_mem());
if (!mem_bio) return 0;
// base64 filter
auto b64_bio = BIO_new(BIO_f_base64());//這個(gè)接口在頭文件 evp.h
if (!b64_bio)
{
BIO_free(mem_bio); //釋放申請(qǐng)成功的空間
return 0;
}
// 形成BIO鏈表
//b64-mem
// 形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果
BIO_push(b64_bio, mem_bio); // 2個(gè)鏈表(從鏈表頭部,代表整個(gè)鏈表)
//write 是編碼 3字節(jié) => 4字節(jié) 不足3字節(jié)補(bǔ)充0 和等于號(hào)
int re = BIO_write(b64_bio, in, len); //將數(shù)據(jù)寫(xiě)入到鏈表頭
if (re < 0)
{
// 寫(xiě)入失敗, 清空整個(gè)鏈表節(jié)點(diǎn)
BIO_free_all(b64_bio);
return 0;
}
// 刷新緩存, 寫(xiě)入鏈表的mem
BIO_flush(b64_bio);
// 從鏈表源內(nèi)存讀取
BUF_MEM* p_data = nullptr; // 需要引入
BIO_get_mem_ptr(b64_bio, &p_data); //拿到編碼數(shù)據(jù)了
int out_size = 0;
if (p_data)
{
memcpy(out_base64, p_data->data, p_data->length);
out_size = p_data->length;
}
//執(zhí)行完后, 清理空間
BIO_free_all(b64_bio);
return out_size;
}
int Base64Decode(const char* in, int len, unsigned char* out_data)
{
if (!in || len <= 0 || !out_data)
{
return 0;
}
// 內(nèi)存源
auto mem_bio = BIO_new_mem_buf(in, len);
if (!mem_bio)
{
return 0;
}
// base64 過(guò)濾器
auto b64_bio = BIO_new(BIO_f_base64());
if (!b64_bio)
{
BIO_free(mem_bio);
return 0;
}
//形成BIO鏈條
BIO_push(b64_bio, mem_bio);
//讀取 解碼 4字節(jié)轉(zhuǎn)3字節(jié)
size_t size = 0;
BIO_read_ex(b64_bio, out_data, len, &size);
BIO_free_all(b64_bio);
return size;
}
int main(int argc, char argv[])
{
cout << "Test openssl BIO base64" << endl;
unsigned char data[] = "測(cè)試Base64數(shù)據(jù)";
int len = sizeof(data);
char out[1024];
int ret = Base64Encode(data, len, out);
if (ret)
{
out[ret] = '\0';
}
cout << "base64:" << out << endl;
unsigned char out_data[1024] = { 0 };
//ret = Base64Decode(out, sizeof(out), out_data);// 這里不能用sizeof() , 用計(jì)算字符長(zhǎng)度
ret = Base64Decode(out, strlen(out), out_data);
cout << "encode :" << out_data << endl;
}
Open SSL
做Base64
編碼換行
Open SSL
做Base64
在做編碼操作的時(shí)候,默認(rèn)情況下遇到64
字節(jié)(不同平臺(tái)不確定,)的時(shí)候就會(huì)進(jìn)行換行操作。
例如將上面的輸入內(nèi)容改得過(guò)長(zhǎng).
unsigned char data[] = "測(cè)試Base64數(shù)據(jù)形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果";
執(zhí)行的編碼結(jié)果就會(huì)出現(xiàn)換行操作, 如下圖所示:
只需要在寫(xiě)入待編碼碼內(nèi)容前進(jìn)行參數(shù)設(shè)置,就可以使其不換行
//超過(guò)長(zhǎng)度不還行
BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);
編碼方法的整體代碼如下:
int Base64Encode(const unsigned char* in, int len, char* out_base64)
{
if (!in || len < 0 || !out_base64)
{
return 0;
}
//創(chuàng)建內(nèi)存源
auto mem_bio = BIO_new(BIO_s_mem());
if (!mem_bio) return 0;
// base64 filter
auto b64_bio = BIO_new(BIO_f_base64());//這個(gè)接口在頭文件 evp.h
if (!b64_bio)
{
BIO_free(mem_bio); //釋放申請(qǐng)成功的空間
return 0;
}
// 形成BIO鏈表
//b64-mem
// 形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果
BIO_push(b64_bio, mem_bio); // 2個(gè)鏈表(從鏈表頭部,代表整個(gè)鏈表)
//超過(guò)長(zhǎng)度不還行
BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);
//write 是編碼 3字節(jié) => 4字節(jié) 不足3字節(jié)補(bǔ)充0 和等于號(hào)
//編碼數(shù)據(jù)每64直接會(huì)加一個(gè)\n換行符號(hào),并且結(jié)尾時(shí)也有換行符號(hào)
int re = BIO_write(b64_bio, in, len); //將數(shù)據(jù)寫(xiě)入到鏈表頭
if (re < 0)
{
// 寫(xiě)入失敗, 清空整個(gè)鏈表節(jié)點(diǎn)
BIO_free_all(b64_bio);
return 0;
}
// 刷新緩存, 寫(xiě)入鏈表的mem
BIO_flush(b64_bio);
// 從鏈表源內(nèi)存讀取
BUF_MEM* p_data = nullptr; // 需要引入
BIO_get_mem_ptr(b64_bio, &p_data); //拿到編碼數(shù)據(jù)了
int out_size = 0;
if (p_data)
{
memcpy(out_base64, p_data->data, p_data->length);
out_size = p_data->length;
}
//執(zhí)行完后, 清理空間
BIO_free_all(b64_bio);
return out_size;
}
執(zhí)行結(jié)果,如下,編碼的內(nèi)容已經(jīng)不會(huì)再換行了。
從上可以看出, 如果沒(méi)有了末尾的換行符號(hào)。解碼的時(shí)候又出現(xiàn)無(wú)法解碼的問(wèn)題, 因?yàn)榻獯a是按照末尾的\n
來(lái)執(zhí)行結(jié)尾的。因?yàn)槿缦拢绻以倬幋a內(nèi)容的后面加上\n
即可正確的進(jìn)行解碼操作了.
int main(int argc, char argv[])
{
cout << "Test openssl BIO base64" << endl;
unsigned char data[] = "測(cè)試Base64數(shù)據(jù)形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果形成鏈表, 往base64內(nèi)存寫(xiě)數(shù)據(jù), 進(jìn)行編碼,結(jié)果會(huì)傳遞到鏈表的下一個(gè)節(jié)點(diǎn), 到mem中讀取結(jié)果";
int len = sizeof(data);
char out[1024];
int ret = Base64Encode(data, len, out);
if (ret)
{
out[ret] = '\0';
}
cout << "base64:" << out << endl;
// 手動(dòng)加下行結(jié)尾的符號(hào)
out[ret] = '\n';
out[ret+1] = '\0';
unsigned char out_data[1024] = { 0 };
//ret = Base64Decode(out, sizeof(out), out_data);// 這里不能用sizeof() , 用計(jì)算字符長(zhǎng)度
ret = Base64Decode(out, strlen(out), out_data);
cout << "encode :" << out_data << endl;
}
另外一種解決辦法就是直接也在解碼代碼中也加上對(duì)換行符號(hào)的忽略.
int Base64Decode(const char* in, int len, unsigned char* out_data)
{
if (!in || len <= 0 || !out_data)
{
return 0;
}
// 內(nèi)存源
auto mem_bio = BIO_new_mem_buf(in, len);
if (!mem_bio)
{
return 0;
}
// base64 過(guò)濾器
auto b64_bio = BIO_new(BIO_f_base64());
if (!b64_bio)
{
BIO_free(mem_bio);
return 0;
}
//形成BIO鏈條
BIO_push(b64_bio, mem_bio);
//取消默認(rèn)讀取換行符號(hào)做結(jié)束的操作
BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);
//讀取 解碼 4字節(jié)轉(zhuǎn)3字節(jié)
size_t size = 0;
BIO_read_ex(b64_bio, out_data, len, &size);
BIO_free_all(b64_bio);
return size;
}
執(zhí)行的結(jié)果都可以O(shè)K了。
綜上問(wèn)題, 編解碼必須要一致。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-774596.html
Base58
由于Base64
的方式存在一些問(wèn)題,比如+
,/
在一些傳輸內(nèi)容時(shí)容易出現(xiàn)特殊字符問(wèn)題,還有0
/O
/o
, l(小寫(xiě)母L)
,I(大寫(xiě)字母i)
在某些情況下肉眼不易區(qū)分。Base58去掉了0(數(shù)字0)
、 O(大寫(xiě)字母o)
、l(小寫(xiě)字母L)
、I(大寫(xiě)字母i)
,以及兩個(gè)特殊字符+
,/
.一共去掉了6個(gè)字符,就剩下了58個(gè)字符。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-774596.html
字節(jié)值 | Base58編碼 | 字節(jié)值 | Base58編碼 | 字節(jié)值 | Base58編碼 | 字節(jié)值 | Base58編碼 | |||
---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 16 | H | 32 | Z | 48 | q | |||
1 | 2 | 17 | J | 33 | a | 49 | r | |||
2 | 3 | 18 | K | 34 | b | 50 | s | |||
3 | 4 | 19 | L | 35 | c | 51 | t | |||
4 | 5 | 20 | M | 36 | d | 52 | u | |||
5 | 6 | 21 | N | 37 | e | 53 | v | |||
6 | 7 | 22 | P | 38 | f | 54 | w | |||
7 | 8 | 23 | Q | 39 | g | 55 | x | |||
8 | 9 | 24 | R | 40 | h | 56 | y | |||
9 | A | 25 | S | 41 | i | 57 | z | |||
10 | B | 26 | T | 42 | j | |||||
11 | C | 27 | U | 43 | k | |||||
12 | D | 28 | V | 44 | m | |||||
13 | E | 29 | W | 45 | n | |||||
14 | F | 30 | X | 46 | o | |||||
15 | G | 31 | Y | 47 | p |
輾轉(zhuǎn)相除法
- 兩個(gè)數(shù)的最大公約數(shù)等于它們中較小的數(shù)和兩數(shù)只差的最大公約數(shù)
- 歐幾里德算法,是求最大公約數(shù)的算法
- 兩個(gè)數(shù)的最大公約數(shù)是指同時(shí)整除它們的最大正整數(shù)。輾轉(zhuǎn)相除法的基本原理是兩個(gè)數(shù)的最大公約數(shù)等于它們中較小的數(shù)和兩數(shù)只差的最大公約數(shù)。
- 如果要將1234轉(zhuǎn)換成58進(jìn)制;
- 1234 除以 58 ,商21, 余數(shù)為16,查表得
H
。 - 用21除以58, 商0, 余數(shù)為21, 查表得
N
。 - 如果待轉(zhuǎn)的數(shù)前面又0直接附加編碼1來(lái)代表,有多少個(gè)就附加多少個(gè)。
Base56輸出字節(jié)數(shù)
- 在編碼后字符串中, 是從58個(gè)字符中選擇,需要表示的位數(shù)是 l o g 2 58 log_{2}58 log2?58, 每一個(gè)字母代表的信息是 l o g 2 58 log_{2}58 log2?58.
- 輸入的字節(jié): (length *8)位。
- 預(yù)留的字符數(shù)量就是 ( l e n g t h ? 8 ) / l o g 2 58 (length*8)/log_{2}58 (length?8)/log2?58
到了這里,關(guān)于【OpenSSL】OpenSSL實(shí)現(xiàn)Base64的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!