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-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 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;
}
// 内存源 source
auto 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-mem
BIO_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;
}
// 刷新缓存,写入链表的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 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 filter
auto 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 <&