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;
}
}
}
暂无评论内容