Open SSL 3.0相关知识以及源码流程分析

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 Studio2017EnterpriseVCAuxiliaryBuild
# 找到合适的编译架构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.0mybuild

# 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位字节(3×8=24)转化为4个6位的字节(4×6=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字节会加
 换行符, 默认结尾有换行符
	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 << "]" << endl;
	}
	cout << "-------解码-------" << endl;
	unsigned char out_decode[1024] = { 0 };
	res = base->base64Decode(out, res, out_decode);
	cout << out_decode << endl;
	return 0;
}

加解密相关

单向散列函数:

1、任意长度数据输出固定长度散列减少匹配开销

2、快速计算、快速验证

3、消息变化引起散列值变化

4、单向不可逆、抗碰撞

常用Hash算法:

MD5、SHA1、SHA2、SHA3、国密SM3

字节序问题: 如0x3132

大端字节:

高位字节在地位字节的前面,存储为0x31 0x32

小端字节:

低位字节在高位字节的前面, 存储为0x32 0x31

md5算法

#include <openssl/md5.h>	
unsigned char data[] = "测试MD5数据";
unsigned char out[1024] = { 0 };
int len = sizeof(data);
MD5_CTX ctx;
// 初始化函数
MD5_Init(&ctx);
// 多次数据添加
MD5_Update(&ctx, data, len);
// md5
MD5_Final(out, &ctx);
for (int i = 0; i < 16; i++)
{
  cout << hex << (int)out[i];
}

获取文件的md5值

std::string getFileListHash(std::string filePath)
{
	std::string hash;
	// 以二进制方式打开文件
	ifstream ifs(filePath, ios::binary);
	if (!ifs)
	{
		return hash;
	}

	// 一次读取多少字节
	int block_size = 128;
	// 文件读取buf
	unsigned char buf[1024] = { 0 };
	// hash输出
	unsigned char out[1024] = { 0 };
	while (!ifs.eof())
	{
		ifs.read((char*)buf, block_size);
		int read_size = ifs.gcount();
		if (read_size <= 0)
		{
			break;
		}
		MD5(buf, read_size, out);
		hash.insert(hash.end(), out, out + 16);
	}
	ifs.close();
	MD5((unsigned char *)hash.data(), hash.size(), out);
	return std::string(out, out + 16);
}

void printHex(string data)
{
	for (auto c : data)
		cout << hex << (int)(unsigned char)c;
	cout << endl;
}

int main() 
{
  auto res = getFileListHash("./md5_test.cpp");
	printHex(res);
  return 0;
}

HMAC

哈希运算消息认证码

#include <openssl/hmac.h>
std::string getHmac()
{
	unsigned char data[1024] = "hmac";
	int data_size = strlen((char *)data);
	unsigned char mac[1024] = { 0 };
	unsigned int mac_size = 0;
	char key[1024] = "123456789";
	HMAC(EVP_sha256(), key, strlen(key), data, data_size, mac, &mac_size);
	string msg(mac, mac + mac_size);
	msg.append(data, data + data_size);
	return msg;
}

分组密码模式:ECB模式和CBC模式

ECB模式:电子密码本模式,各个分组单独加密

CBC模式:密文分组链接模式,初始化向量和明文分组一异或然后加密,加密后的数据与后一组明文异或再加密,以此类推。

哈希封装

// HashTools.h
#pragma once
#include<iostream>
#include <openssl/md5.h>
#include <openssl/sha.h>

class HashTools
{
public:
	enum HashType {
		HashType_MD5,
		HashType_SHA1,
		HashType_SHA224,
		HashType_SHA256,
		HashType_SHA384,
		HashType_SHA512
	};
	HashTools(HashType type);
	void add(std::string data);
	std::string result();
	~HashTools();

private:
	HashType type;
	MD5_CTX md5_ctx;
	SHA_CTX sha1_ctx;
	SHA256_CTX sha256_ctx;
	SHA512_CTX sha512_ctx;
};

// HashTools.cpp
#include "HashTools.h"

#include <openssl/sha.h>

using namespace std;

HashTools::HashTools(HashType type)
{
	this->type = type;
	switch (type)
	{
	case HashTools::HashType_MD5:
		MD5_Init(&md5_ctx);
		break;
	case HashTools::HashType_SHA1:
		SHA1_Init(&sha1_ctx);
		break;
	case HashTools::HashType_SHA224:
		SHA224_Init(&sha256_ctx);
		break;
	case HashTools::HashType_SHA256:
		SHA256_Init(&sha256_ctx);
		break;
	case HashTools::HashType_SHA384:
		SHA384_Init(&sha512_ctx);
		break;
	case HashTools::HashType_SHA512:
		SHA512_Init(&sha512_ctx);
		break;
	default:
		break;
	}
}

void HashTools::add(std::string data)
{
	switch (type)
	{
	case HashTools::HashType_MD5:
		MD5_Update(&md5_ctx, data.c_str(), data.length());
		break;
	case HashTools::HashType_SHA1:
		SHA1_Update(&sha1_ctx, data.c_str(), data.length());
		break;
	case HashTools::HashType_SHA224:
		SHA224_Update(&sha256_ctx, data.c_str(), data.length());
		break;
	case HashTools::HashType_SHA256:
		SHA256_Update(&sha256_ctx, data.c_str(), data.length());
		break;
	case HashTools::HashType_SHA384:
		SHA384_Update(&sha512_ctx, data.c_str(), data.length());
		break;
	case HashTools::HashType_SHA512:
		SHA512_Update(&sha512_ctx, data.c_str(), data.length());
		break;
	default:
		break;
	}
}

std::string HashTools::result()
{
	unsigned char out[1024] = { 0 };
	int len = MD5_DIGEST_LENGTH;
	switch (type)
	{
	case HashTools::HashType_MD5:
		MD5_Final(out, &md5_ctx);
		break;
	case HashTools::HashType_SHA1:
		len = SHA_DIGEST_LENGTH;
		SHA1_Final(out, &sha1_ctx);
		break;
	case HashTools::HashType_SHA224:
		len = SHA224_DIGEST_LENGTH;
		SHA224_Final(out, &sha256_ctx);
		break;
	case HashTools::HashType_SHA256:
		len = SHA256_DIGEST_LENGTH;
		SHA256_Final(out, &sha256_ctx);
		break;
	case HashTools::HashType_SHA384:
		len = SHA384_DIGEST_LENGTH;
		SHA384_Final(out, &sha512_ctx);
		break;
	case HashTools::HashType_SHA512:
		len = SHA512_DIGEST_LENGTH;
		SHA512_Final(out, &sha512_ctx);
		break;
	default:
		break;
	}
	char* res = new char[len * 2 + 1];
	for (int i = 0; i < len; i++)
	{
		sprintf(&res[i * 2], "%02x", out[i]);
	}
	std::string result = std::string(res);
	delete[] res;
	return result;
}

HashTools::~HashTools()
{

}

// main.cpp 测试
int main(int argc, char* argv[])
{
	HashTools tools(HashTools::HashType_SHA512);
	tools.add("dafddsafd阿斯顿发多少");
	string res = tools.result();
	cout << "res = " << res << " length = " << res.length() << endl;
	return 0;
}

RSA

RSA算法秘钥长度越长,安全性越好,加解密所需的时间越长。

秘钥长度增长一倍,公钥操作所需的时间增长约4倍,私钥操作所需的时间增加约8倍,公钥生成时间约增长16倍。

公钥加密,私钥解密。私钥加密,公钥解密。

加密强度高,效率低,运用于对称秘钥的加密传输,不会用于加密特别大的数据。

1、秘钥分发(对称加密)

核心思想:公钥加密,私钥解密

分发步骤:

假设AB两端

A端生成了一个秘钥对,分发公钥给B端

B端生成一个对称加密的秘钥,使用公钥加密,发送给A端

A端接收密文利用私钥解密获取对称加密秘钥

2、签名(验证数据是否被篡改)

核心思想:私钥加密,公钥解密。

假设AB两端

A端生成了一个秘钥对,分发公钥给B端

签名:

A对原始数据进行哈希运算,使用私钥对哈希值加密,将原始数据和密文发送给B

校验签名:

B接收数据:密文加收到的原始数据

使用公钥对密文解密获取接收到的哈希值

使用哈希算法对收到的数据进行哈希运算,然后将结果和解密的哈希值对比

// 接口说明

/*
写入文件中的公私钥数据是base64编码之后的数据
fp: 磁盘文件,需要指定写的权限
x: 秘钥对
enc: 指定加密算法(对称加密)
kstr: 对称加密的秘钥
klen:秘钥长度
cb:回调函数
u:回调函数的参数
*/
int PEM_write_RSAPrivateKey(FILE *fp, RSA *x, const EVP_CIPHER *enc,
                                        unsigned char *kstr, int klen,
                                        pem_password_cb *cb, void *u);
// 从文件读取私钥
RSA *PEM_read_RSAPrivateKey(FILE *fp, RSA **x,
                                        pem_password_cb *cb, void *u);

// 代码示例:

#include <openssl/rsa.h>
#include <openssl/pem.h>

RSA* rsa = RSA_new();
// 创建bignum对象
BIGNUM *e = BN_new();
BN_set_word(e, 12345);
// 生成密钥对(存储在内存中)  bit(单位bit,常用的长度 1024 * n) e 比较大的数(5位以内的) cb回调函数
RSA_generate_key_ex(rsa, 1024, e, NULL);

// 将密钥对写入文件  方法一:
//FILE *fp = fopen("public.pem", "w");
//PEM_write_RSAPublicKey(fp, rsa);
//fclose(fp);

//fp = fopen("private.pem", "w");
//PEM_write_RSAPrivateKey(fp, rsa, NULL, NULL, 0, NULL, NULL);
//fclose(fp);

// 将密钥对写入文件 bio接口
BIO* bio = BIO_new_file("public_bio.pem", "w");
PEM_write_bio_RSAPublicKey(bio, rsa);
// 释放资源
BIO_free(bio);
bio = BIO_new_file("private_bio.pem", "w");
PEM_write_bio_RSAPrivateKey(bio, rsa, NULL, NULL, 0, NULL, NULL);
BIO_free(bio);

// 提取公私钥
RSA* publicKey = RSAPublicKey_dup(rsa);
RSA* privateKey = RSAPrivateKey_dup(rsa);
// 签名使用 私钥加密 公钥解密
/*
flen:加密/解密的数据长度 0 < flen <= 秘钥长度 - 11
from:加密/解密的数据
to:数据 加密->密文 解密->明文
rsa:秘钥
padding:填充方案RSA_PKCS1_PADDING 填充11字节
*/
int RSA_private_encrypt(int flen, unsigned char *
                        from:,
                         unsigned char *to, RSA *rsa, int padding);

int RSA_public_decrypt(int flen, unsigned char *from,
                        unsigned char *to, RSA *rsa, int padding);

// 加密加密  公钥加密 私钥解密
int RSA_public_encrypt(int flen, const unsigned char *from,
                       unsigned char *to, RSA *rsa, int padding);

int RSA_private_decrypt(int flen, const unsigned char *from,
                        unsigned char *to, RSA *rsa, int padding);

// 签名和校验接口
/*
type:指定使用的哈希算法 NID_MD5/NID_SHA1/...
m:要进行签名的数据
m_len:签名数据的长度
sigret:签名之后的数据
siglen:签名之后的数据长度
rsa:私钥
*/
int RSA_sign(int type, const unsigned char *m, unsigned int m_len,
  unsigned char *sigret, unsigned int *siglen, RSA *rsa);

/*
type:指定使用的哈希算法 NID_MD5/NID_SHA1
m:要进行签名的数据
m_len:签名数据的长度
sigbuf:要校验的签名数据
siglen:要校验的签名数据长度
rsa:公钥
返回值 == 1 成功
*/
int RSA_verify(int type, const unsigned char *m, unsigned int m_len,
  unsigned char *sigbuf, unsigned int siglen, RSA *rsa);
// 代码测试
// 加解密测试
string RsaTools::encryptPublicKeyData()
{
	string text = "测试加密的数据";
	RSA* publicKey = RSA_new();
	BIO* bio = BIO_new_file("public.pem", "r");
	PEM_read_bio_RSAPublicKey(bio, &publicKey, NULL, NULL);
	BIO_free(bio);

	int keyLen = RSA_size(publicKey);
	char* buf = new char[keyLen];
	int len = RSA_public_encrypt(text.size(), (const unsigned char*)text.data(), (unsigned char *)buf, publicKey, RSA_PKCS1_PADDING);
	if (len < 0)
	{
		return "";
	}
	string result = string(buf, len);
	delete[] buf;
	return result;
}

string RsaTools::decryptPrivateData(string enData)
{
	RSA* privateKey = RSA_new();
	BIO* bio = BIO_new_file("private.pem", "r");
	PEM_read_bio_RSAPrivateKey(bio, &privateKey, NULL, NULL);
	BIO_free(bio);

	int keyLen = RSA_size(privateKey);
	char* buf = new char[keyLen];
	int len = RSA_private_decrypt(enData.length(), (const unsigned char *)enData.data(), (unsigned char *)buf, privateKey, RSA_PKCS1_PADDING);
	if (len < 0)
	{
		return "";
	}
	string result = string(buf, len);
	delete[] buf;
	return result;
}


// 签名验签
string text = "测试签名的数据";
RSA* privateKey = RSA_new();
BIO* bio = BIO_new_file("private.pem", "r");
PEM_read_bio_RSAPrivateKey(bio, &privateKey, NULL, NULL);
BIO_free(bio);

int keyLenPri = RSA_size(privateKey);
unsigned char* buf = new unsigned char[keyLenPri];
unsigned int outLen = 0;
// 签名
RSA_sign(NID_sha1, (const unsigned char *)text.data(), text.length(), buf, &outLen, privateKey);
string signResult = string((char *)buf, outLen);
cout << "signResult = " << signResult << endl;

cout << "------------------------" << endl;

RSA* publicKey = RSA_new();
bio = BIO_new_file("public.pem", "r");
PEM_read_bio_RSAPublicKey(bio, &publicKey, NULL, NULL);
BIO_free(bio);
/*int verifyRes = RSA_verify(NID_sha1, (const unsigned char *)text.data(), text.length(),
  (const unsigned char *)signResult.data(), signResult.length(), publicKey);*/
int verifyRes = RSA_verify(NID_sha1, (const unsigned char *)text.data(), text.length(),
  buf, outLen, publicKey);
cout << "verifyRes = " << verifyRes << endl;

// 封装见代码库

对称加密

对称秘钥加密法主要基于块加密,选取固定长度的秘钥去加密明文中固定长度的块,生成的密文块与铭文块长度一样。

AES中规定块的长度为128bit,而秘钥长度可以选择128,192,256bit。

/*
userKey 秘钥字符串 长度可选16byte/24byte/32byte
bits 秘钥长度
key 传出参数:保存了设置的秘钥信息
*/
int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);

/*
in 要加密/解密的数据
out 传出参数 加密:存储密文 解密:存储明文
key 传出参数:保存了设置的秘钥信息
length 修改in的长度 看是否能被16整除,不能被整除实际长度((len / 16) + 1) * 16
key 初始化之后的秘钥
ivec 初始化向量 长度和分组长度相同
enc 指定数据加密还是解密 AES_ENCRYPT=1  AES_DECRYPT=0
*/
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, size_t length, const AES_KEY *key, unsigned char *ivec, const int enc);
// 测试代码
#include <openssl/aes.h>

const char* data = "afjnkasj的数据asfad";
const char* key = "1234567887654321";
AES_KEY encKey;
AES_set_encrypt_key((const unsigned char *)key, 128, &encKey);
// 计算长度
int length = 0;
int len = strlen((char *)data + 1);
if (len % 16 != 0)
{
  length = ((len / 16) + 1) * 16;
}
else {
  length = len;
}
unsigned char* out = new unsigned char[length];
unsigned char ivec[AES_BLOCK_SIZE];
memset(ivec, 9, sizeof(ivec));
AES_cbc_encrypt((const unsigned char *)data, out, length, &encKey, ivec, AES_ENCRYPT);
cout << "加密的数据 out = " << out << endl;

// 解密
unsigned char* dec_data = new unsigned char[length];
AES_KEY deckey;
memset(ivec, 9, sizeof(ivec));
AES_set_decrypt_key((const unsigned char *)key, 128, &deckey);
AES_cbc_encrypt(out, dec_data, length, &deckey, ivec, AES_DECRYPT);
cout << "还原的数据 out = " << dec_data << endl;


// 封装版本的使用
string data1 = "afjnkasj的数据asdfadasdfad1234";
string key1 = "1234567887654321";
AESTools tools;
try
{
  string enString = tools.encrypt(data1, key1);
  cout << "enString = " << enString << endl;
  cout << "----------------------------" << endl;

  // 解密
  string deString = tools.decrypt(enString, key1);
  cout << "deString = " << deString << endl;
  cout << "----------------------------" << endl;
}
catch (const std::exception& e)
{
  cout << "exception = " << e.what() << endl;
}

OpenSSL SSL/TLS 通讯

利用openssl生成自签名证书

# 自签名根证书
# 根证书私钥:
openssl genrsa -out root.key 2048

# 根证书请求文件:
openssl req -new -out root.csr -key root.key

# 根证书
openssl x509 -req -in root.csr -out root.crt -signkey root.key -CAcreateserial -days 365

# 生成自签名服务器端证书
openssl genrsa -out server.key 2048
openssl req -new -out server.csr -key server.key
# 方法一
openssl x509 -req -in server.csr -out server.crt -signkey server.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650
# 方法二
openssl x509 -req -in server.csr -out server.crt -CA root.crt -CAkey root.key -CAcreateserial -days 3650
# 不设置根证书
openssl x509 -req -in server.csr -out server.crt -signkey server.key -days 3650

#生成自签名客户端端证书
openssl genrsa -out client.key 2048
openssl req -new -out client.csr -key client.key
openssl x509 -req -in client.csr -out client.crt -signkey client.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650
openssl x509 -req -in client.csr -out client.crt -CA root.crt -CAkey root.key -CAcreateserial -days 3650
# 导出p12文件
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12

证书格式:

pem:是明文格式的,以BEGIN CERTIFICATE开头END CERTIFICATE结尾,中间是经过Base64编码的内容。

DER:是二进制格式的证书

OpenSSL相关接口

// openssl库的初始化,用于取消默认,定制openssl参数
OPENSSL_init_ssl() 

//上下处理接口
SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth);
// 1、初始化ssl通信上下文,需要制定协议和客户端服务端
TLS_client_method
TLS_server_method
  
// 创建ssl
SSL *SSL_new(SSL_CTX *ctx); 
// 设置fd
int	SSL_set_fd(SSL *s, int fd); 
// 设置ssl工作在客户端模式
void SSL_set_connect_state(SSL *s)
// 建立连接
int SSL_connect(SSL *ssl); // client
int SSL_do_handshake(SSL *s);
int SSL_accept(SSL *s) // server
// 发送数据
int SSL_write(SSL *ssl,const void *buf,int num); 
// 接收数据
int SSL_read(SSL *ssl,void *buf,int num); 
// 进行清理工作
int SSL_shutdown(SSL *s); 
void SSL_CTX_free(SSL_CTX *);
void SSL_free(SSL *ssl);

// 设置验证服务端证书模式以及回调
void SSL_set_verify(SSL *s, int mode, SSL_verify_cb callback);
// 加载信任证书
int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath)
// 加载证书文件
int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type)
// 加载私钥文件
int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type)
// 检查私钥key
int SSL_CTX_check_private_key(const SSL_CTX *ctx)

OpenSSL https 流程图

OpenSSL源码分析

OpenSSL https连接流程每一步做了什么?

1、SSL_CTX_new源码分析

SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth);

#ifdef SSLVPN_GM
const SSL_METHOD *GM_SSL_client_method(void);	/* GM_SSL */
#endif

GM_SSL_client_metho()中的底层源码做了什么呢?

#ifdef SSLVPN_GM
IMPLEMENT_tls_meth_func(GM_VERSION, 
                        GM_SSL_client_method,
                        ssl_undefined_function,
                        ssl3_connect,
                        tls1_get_client_method, TLSv1_1_enc_data)
#endif
  
# define IMPLEMENT_tls_meth_func( 
version, 
func_name, 
s_accept, 
s_connect, 
s_get_meth, 
enc_data) 

// s_accept = ssl_undefined_function   空函数 SSLerr 输出
// s_connect = ssl3_connect
// s_get_meth = tls1_get_client_method
// enc_data = TLSv1_1_enc_data
  
const SSL_METHOD *func_name(void)  
        { 
        static const SSL_METHOD func_name##_data= { 
                version, 
                tls1_new, 
                tls1_clear, 
                tls1_free, 
                s_accept, 
                s_connect, 
                ssl3_read, 
                ssl3_peek, 
                ssl3_write, 
                ssl3_shutdown, 
                ssl3_renegotiate, 
                ssl3_renegotiate_check, 
                ssl3_get_message, 
                ssl3_read_bytes, 
                ssl3_write_bytes, 
                ssl3_dispatch_alert, 
                ssl3_ctrl, 
                ssl3_ctx_ctrl, 
                ssl3_get_cipher_by_char, 
                ssl3_put_cipher_by_char, 
                ssl3_pending, 
                ssl3_num_ciphers, 
                ssl3_get_cipher, 
                s_get_meth, 
                tls1_default_timeout, 
                &enc_data, 
                ssl_undefined_void_function, 
                ssl3_callback_ctrl, 
                ssl3_ctx_callback_ctrl, 
        }; 
        return &func_name##_data; 
        }

SSL_CTX_new中做的事情?

SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
{
  SSL_CTX *ret = NULL;
  ret = (SSL_CTX *)OPENSSL_malloc(sizeof(SSL_CTX));
  ret->method = meth; // ret->method = GM_SSL_client_method()
  retrun ret;
}

2、SSL_new的源码分析

SSL *SSL_new(SSL_CTX *ctx) 
{
  SSL *s;
  s = (SSL *)OPENSSL_malloc(sizeof(SSL));
  s->options = ctx->options;
  s->mode = ctx->mode;
  // ...省略
  s->method = ctx->method; // s->method = GM_SSL_client_method()
  return s;
}

3、SSL_set_fd函数源码分析

int	SSL_set_fd(SSL *s, int fd)
{
    int ret = 0;
    BIO *bio = NULL;
    bio = BIO_new(BIO_s_socket());
    BIO_set_fd(bio, fd, BIO_NOCLOSE);
    SSL_set_bio(s, bio, bio); // ssl 关联 socket fd
    ret = 1;
    return (ret);
}

4、SSL_set_connect_state、SSL_do_handshake源码分析

void SSL_set_connect_state(SSL *s)
{
    s->server = 0;
    s->shutdown = 0;
    s->state = SSL_ST_CONNECT | SSL_ST_BEFORE; // 设置当前的状态
    s->handshake_func = s->method->ssl_connect; // s->handshake_func = ssl3_connect
}

int SSL_do_handshake(SSL *s)
{
    int ret = 1;
    if (SSL_in_init(s) || SSL_in_before(s)) {
        ret = s->handshake_func(s); // s->handshake_func(s) = ssl3_connect(s)
    }
    return (ret);
}

5、SSL_connect函数源码分析

int SSL_connect(SSL *ssl)
{
  if (s->handshake_func == 0)
    SSL_set_connect_state(s);
  return (s->method->ssl_connect(s)); // s->method->ssl_connect(s) = ssl3_connect(s)
}

// 这里相当于就是调用了函数SSL_set_connect_state和SSL_do_handshake

6、SSL_accept函数的源码分析

int SSL_accept(SSL *s)
{
  if (s->handshake_func == 0)
        SSL_set_accept_state(s);
  return (s->method->ssl_accept(s)); // ssl_accept(s) = ssl3_accept(s)
}

7、剩余函数的分析(SSL_write、SSL_read、SSL_shutdown等)

int SSL_write(SSL *s, const void *buf, int num)
{
    return (s->method->ssl_write(s, buf, num)); // ssl3_write
}
int SSL_read(SSL *s, void *buf, int num)
{
    return (s->method->ssl_read(s, buf, num)); // ssl3_read
}
int SSL_shutdown(SSL *s)
{
    return s->method->ssl_shutdown(s); // ssl3_shutdown
}

8、ssl3_connect中协商流程的分析

    Client                                          Server

  ClientHello                  -------->
                                                  ServerHello
                                                 Certificate
                                           ServerKeyExchange
                                          CertificateRequest
                               <--------      ServerHelloDone
  Certificate
  ClientKeyExchange
  CertificateVerify
  [ChangeCipherSpec]
  Finished                     -------->
                                           [ChangeCipherSpec]
                               <--------             Finished
  Application Data             <------->     Application Data
int ssl3_connect(SSL *s)
{
  for (;;) {
    // SSL_set_connect_state函数中  s->state = SSL_ST_CONNECT | SSL_ST_BEFORE;
  	switch (s->state) {
        case SSL_ST_BEFORE | SSL_ST_CONNECT:
        	// 初始化操作
          s->state = SSL3_ST_CW_CLNT_HELLO_A;
        case SSL3_ST_CW_CLNT_HELLO_A:
          ret = ssl3_client_hello(s);
          s->state = SSL3_ST_CR_SRVR_HELLO_A;
        case SSL3_ST_CR_SRVR_HELLO_A:
          ret = ssl3_get_server_hello(s);
          s->state = SSL3_ST_CR_CERT_A;
        case SSL3_ST_CR_CERT_A:
          ret = ssl3_get_server_certificate(s);
          s->state = SSL3_ST_CR_KEY_EXCH_A;
        case SSL3_ST_CR_KEY_EXCH_A:
          ret = ssl3_get_key_exchange(s);
          s->state = SSL3_ST_CR_CERT_REQ_A;
        case SSL3_ST_CR_CERT_REQ_A:
          ret = ssl3_get_certificate_request(s);
          s->state = SSL3_ST_CR_SRVR_DONE_A;
        case SSL3_ST_CR_SRVR_DONE_A:
          ret = ssl3_get_server_done(s);
          s->state = SSL3_ST_CW_CERT_A;
        case SSL3_ST_CW_CERT_A:
          ret = ssl3_send_client_certificate(s);
          s->state = SSL3_ST_CW_KEY_EXCH_A;
        case SSL3_ST_CW_KEY_EXCH_A:
          ret = ssl3_send_client_key_exchange(s);
          s->state = SSL3_ST_CW_CERT_VRFY_A;
        case SSL3_ST_CW_CERT_VRFY_A:
          ret = ssl3_send_client_verify(s);
          s->state = SSL3_ST_CW_CHANGE_A;
        case SSL3_ST_CW_CHANGE_A:
          ret = ssl3_send_change_cipher_spec();
          s->state = SSL3_ST_CW_FINISHED_A;
        case SSL3_ST_CW_FINISHED_A:
          ret = ssl3_send_finished()
          s->state = SSL3_ST_CW_FLUSH;
          s->s3->tmp.next_state = SSL3_ST_CR_FINISHED_A;
        case SSL3_ST_CW_FLUSH:
          s->state = s->s3->tmp.next_state; // SSL3_ST_CR_FINISHED_A
        case SSL3_ST_CR_FINISHED_A:
          ret = ssl3_get_finished();
          s->state = SSL_ST_OK;
        case SSL_ST_OK:
          ssl3_cleanup_key_block();
          goto end;
    }
  }
}
© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容