Open SSL 3.0相關知識以及源碼流程分析
編譯
- windows環境編譯
1、工具安裝
安裝安裝perl腳本解釋器、安裝nasm匯編器(添加到環境變量)、Visual Studio編譯工具
安裝dmake
ppm install dmake # 需要過墻
2、開始編譯
# 1、找到Visual Studio命令行編譯工具目錄 或者菜單欄直接啟動對應架構的cmd程序
# D:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build
# 找到合適的編譯架構bat 雙擊 比如:vcvars32.bat# 2、進入openssl目錄
cd D:\openssl-openssl-3.1.0# 3、perl生成對應的makefile
# -prefix 是編譯后輸出的路徑,默認會生成到C:\Program Files (x86)目錄
perl Configure VC-WIN32 --prefix=D:\openssl-openssl-3.1.0\mybuild# 4、編譯等待
nmake# 5、安裝到指定目錄
# 編譯好的文件安裝到指定目錄,默認是C:\Program Files (x86)\OpenSSL,如果是在C盤,運行控制臺是需要有管理員權限
nmake install
- Linux編譯
# 1、解壓進入目錄
# 2、config配置
./config
# 3、編譯
make
# 4、安裝庫到指定目錄 /usr/local/include/openssl /usr/local/lib
make install
- 基礎使用
Open SSL 3.0支持國密sm2 sm3 sm4
包含對稱加密、非對稱加密、單向散列、偽隨機、簽名、密碼交換、證書等一系列算法庫。
applink錯誤處理
解決辦法:
// 屬性配置 -> C++ -> 預處理器 _CRT_SECURE_NO_WARNINGS
extern "C"
{#include <openssl/applink.c>
};
編碼原理
- Base16
用16進制來編碼
static const char BASE16_ENC_TAB[] = "0123456789ABCDEF";
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-503, 4, 5, 6, 7, 8, 9, -1, -1, -1, // 51-60-1, -1, -1, -1, 10, 11, 12, 13, 14, 15, // 61-70
};int base16Encode(const unsigned char* in, int size, char* out)
{for (int i = 0; i < size; i++){// 一個字節取出高4位和低4位char h = in[i] >> 4; // 移位丟棄低位char low = in[i] & 0x0F; // & 去掉高位out[i * 2] = BASE16_ENC_TAB[h]; // 0~15映射到對應字符串out[i * 2 + 1] = BASE16_ENC_TAB[low];}// base16轉碼后空間擴大一倍 4位轉成一個字符 1個字符轉成2個字符return size * 2;
}int base16Decode(const string& in, unsigned char* out)
{// 將兩個字符拼接成一個字符for (int i = 0; i < in.size(); i += 2){unsigned char ch = in[i]; // 高位轉換的字符unsigned char cl = in[i + 1]; // 低位轉換的字符unsigned char h = BASE16_DEC_TAB[ch]; // 轉換成原來的值unsigned char l = BASE16_DEC_TAB[cl];// 兩個4位拼成一個字符out[i / 2] = h << 4 | l;}return in.size() / 2;
}int main(int argc, char* argv[])
{const unsigned char data[] = "測試Base16";int len = sizeof(data);char out1[1024] = { 0 };unsigned char out2[1024] = { 0 };cout << data << endl;int encode_result = base16Encode(data, len, out1);cout << "encode_result = " << encode_result << " out1:" << out1 << endl;int decode_result = base16Decode(out1, out2);cout << "decode_result = " << decode_result << " out2:" << out2 << endl;getchar();return 0;
}
- Base64
二進制轉字符串
原理:
把3個8位字節(3x8=24)轉化為4個6位的字節(4x6=24),之后在6位的前面補兩個0,形成8位一個字節的形式。如果剩下的字符不足3個字節,則用0填充,輸出字符使用“=”,因此編碼后輸出的文本末尾可能會出現1或者2個“=”。
- Open SSL bio接口
使用bio接口實現base64編碼
#include "Base64.h"
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>
#include <iostream>int Base64::base64Encode(const unsigned char* in, int len, char* out_base64)
{if (!in || len <= 0 || !out_base64){return 0;}// 內存源 sourceauto mem_bio = BIO_new(BIO_s_mem());if (!mem_bio){return 0;}// base64 filter// BIO_new創建bio對象// BIO_f_base64封裝了base64編碼方法的BIO,寫的時候編碼,讀的時候解碼// BIO_s_mem 封裝了內存操作的bio接口,包括對內存的讀寫操作auto b64_bio = BIO_new(BIO_f_base64());if (!b64_bio){BIO_free(mem_bio);return 0;}// 形成bio鏈,連接兩個對象到鏈表中b64_bio->mem_bio// b64-memBIO_push(b64_bio, mem_bio);// 設置超過64字節不添加換行符, 解碼需要對應的設置BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);// 寫入到base64 filter進行編碼,結果會傳遞到鏈表的下一個節點// 到mem中讀取結果(鏈表頭部代表了整個鏈表)// BIO_write 編碼 3字節-> 4字節 不足3字節補充0和=// 編碼數據每64字節會加\n 換行符, 默認結尾有換行符int re = BIO_write(b64_bio, in, len);if (re <= 0){// 釋放整個鏈表節點BIO_free_all(b64_bio);return 0;}// 刷新緩存,寫入鏈表的memBIO_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 Base64::base64Decode(const char* in, int len, unsigned char* out_data)
{if (!in || len <= 0 || !out_data){return 0;}// 內存源 密文// 創建一個內存型的bio對象auto mem_bio = BIO_new_mem_buf(in, len);if (!mem_bio){return 0;}// base64 filterauto b64_bio = BIO_new(BIO_f_base64());if (!b64_bio){BIO_free(mem_bio);return 0;}BIO_push(b64_bio, mem_bio);// 與編碼對應BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);// 讀取解碼size_t size = 0;// BIO_read_ex 從bio接口讀出len字節到buf中int re = BIO_read_ex(b64_bio, out_data, len, &size);BIO_free_all(b64_bio);return size;
}// main.cpp
int main(int argc, char* argv[])
{Base64 *base = new Base64();const unsigned char data[] = "測試Base64阿斯頓發到付親戚212阿發的順豐到付412341324321432141243按時發放";int len = sizeof(data);char out[1024] = { 0 };int res = base->base64Encode(data, len, out);if (res > 0){cout << "[" << out <&