开源加密库Openssl 剖析&实战

本文详细介绍了OpenSSL,包括其主要功能、源码结构和常用编程方法。OpenSSL是一个开源的软件库包,用于实现安全通信,避免窃听并验证通信双方的身份。它提供了SSL协议实现、多种加密算法、哈希函数和公钥算法如RSA。OpenSSL的源码结构包括eay库、ssl库、工具源码、范例源码和测试源码。文章还列举了哈希表、BIO、BASE64编解码、摘要算法(如MD5和SHA1)以及RSA的使用示例。此外,还介绍了RSA的密钥生成、加密解密、签名验证等操作。

一、OpenSSL简介

在计算机网络上,OpenSSL是一个开放源代码的软件库包,应用程序可以使用这个包来进行安全通信,避免窃听,同时确认另一端连接者的身份。

它提供的主要功能有: SSL协议实现(包括SSLv2、 SSLv3和TLSv1)、大量软算法(对称/非对称/摘要)、大数运算、非对称算法密钥生成、 ASN.1编解码库、证书请求(PKCS10)编解码、数字证书编解码、 CRL编解码、OCSP协议、数字证书验证、 PKCS7标准实现和PKCS12个人数字证书格式实现等功能。OpenSSL采用C语言作为开发语言,这使得它具有优秀的跨平台性能。 OpenSSL支持Linux、 UNIX、 windows、 Mac等平台。广泛被应用在互联网的网页服务器上。


1.1、OpenSSL安装

下载路径https://www.OpenSSL.org/source/
在这里插入图片描述

安装步骤

$ tar zxvf OpenSSL-1.1.1i.tar.gz
$ cd OpenSSL-1.1.1i
$ ./config --prefix=/usr/local/OpenSSL
$ make
$ sudo make install


1.2、OpenSSL源码结构

OpenSSL源代码主要由eay库、 ssl库、工具源码、范例源码以及测试源码组成。

(一)eay库
eay库是基础的库函数,提供了很多功能。源代码放在crypto目录下。包括如下内容:
在这里插入图片描述

  1. asn1 DER编码解码(crypto/asn1目录),包含了基本asn1对象的编解码以及数字证书请求、数字证书、 CRL撤销列表以及PKCS8等最基本的编解码函数。这些函数主要通过宏来实现。

  2. 抽象IO(BIO,crypto/bio目录),本目录下的函数对各种输入输出进行抽象,包括文件、内存、标准输入输出、 socket和SSL协议等。

  3. 大数运算(crypto/bn目录),本目录下的文件实现了各种大数运算。这些大数运算主要用于非对称算法中密钥生成以及各种加解密操作。另外还为用户提供了大量辅助函数,比如内存与大数之间的相互转换。

  4. 字符缓存操作(crypto/buffer目录)

  5. 配置文件读取(crypto/conf目录), OpenSSL主要的配置文件为OpenSSL.cnf。本目录下的函数实现了对这种格式配置文件的读取操作。

  6. DSO(动态共享对象,crypto/dso目录),本目录下的文件主要抽象了各种平台的动态库加载函数,为用户提供统一接口。

  7. 硬件引擎(crypto/engine目录),硬件引擎接口。用户如果要写自己的硬件引擎,必须实现它所规定的接口。

  8. 错误处理(crypto/err目录),当程序出现错误时, OpenSSL能以堆栈的形式显示各个错误。本目录下只有基本的错误处理接口,具体的的错误信息由各个模块提供。各个模块专门用于错误处理的文件一般为*_err…c文件。

  9. 对称算法、非对称算法及摘要算法封装(crypto/evp目录)。

  10. HMAC(crypto/hmac目录),实现了基于对称算法的MAC。

  11. hash表(crypto/lhash目录),实现了散列表数据结构。 OpenSSL中很多数据结构都是以散列表来存放的。比如配置信息、 ssl session和asn.1对象信息等。

  12. 数字证书在线认证(crypto/ocsp目录),实现了ocsp协议的编解码以及证书有效性计算等功能。

  13. PEM文件格式处理(crypto/pem),用于生成和读取各种PEM格式文件,包括各种密钥、数字证书请求、数字证书、 PKCS7消息和PKCS8消息等。

  14. pkcs7消息语法(crypto/pkcs7目录),主要实现了构造和解析PKCS7消息;

  15. pkcs12个人证书格式(crypto/pckcs12目录),主要实现了pkcs12证书的构造和解析。

  16. 队列(crypto/pqueue目录),实现了队列数据结构,主要用于DTLS。

  17. 随机数(crypto/rand目录),实现了伪随机数生成,支持用户自定义随机数生成。

  18. 堆栈(crypto/stack目录),实现了堆栈数据结构。

  19. 线程支持(crypto/threads), OpenSSL支持多线程,但是用户必须实现相关接口。

  20. 文本数据库(crypto/txt_db目录)。

  21. x509数字证书(crypto/x509目录和crypto/x509v3),包括数字证书申请、数字证书和CRL的构造、解析和签名验证等功能;

  22. 对称算法(crypto/aes、 crypto/bf、 crypto/cast、 ccrypto/omp和crypto/des等目录)。

  23. 非对称算法(crypto/dh、 crypto/dsa、 crypto/ec和crypto/ecdh)。

  24. 摘要算法(crypto/md2、 crypto/md4、 crypto/md5和crypto/sha)以及密钥交换/认证算法(crypto/dh 和crypto/krb5)。


(二)ssl库
ssl库所有源代码在ssl目录下,包括了sslv2、 sslv3、 tlsv1和DTLS的源代码。各个版本基本上都有客户端源码(* _clnt.c)、服务源码(* _srvr.c)、通用源码(* _both.c)、底层包源码( * _pkt.c)、方法源码(* _meth.c)以及协议相关的各种密钥计算源码(* _enc.c)等,都很有规律。
在这里插入图片描述
在这里插入图片描述


(三)工具源码
工具源码主要在crypto/apps目录下,默认编译时只编译成OpenSSL可执行文件。该命令包含了各种命令工具。此目录下的各个源码可以单独进行编译。
在这里插入图片描述


(四)范例源码
范例源码在demo目录下,另外engines目录给出了OpenSSL支持的几种硬件的engines源码,也可以作为engine编写参考。
在这里插入图片描述


(五)测试源码
测试源码主要在test目录下。
在这里插入图片描述


1.3、OpenSSL学习方法

1)建立学习环境
建立一个供调试的OpenSSL环境 linux或者其他平台。


2)学习OpenSSL的命令
通过OpenSSL命令的学习,对OpenSSL有基本的了解。


3)学习OpenSSL源代码并调试
对于OpenSSL函数的学习,主要查看OpenSSL自身是如何调用的,或者查看函数的实现。对于OpenSSL中只有实现而没有调用的函数,读者需要自己写源码或研究源代码去学习。主要需要学习的源代码有:

  • apps目录下的各个程序,对应于OpenSSL的各项命令;
  • demos下的各种源代码;
  • engines下的各种engine实现;
  • test目录下的各种源代码。


4)学会使用 OpenSSL 的 asn.1 编解码
OpenSSL中很多函数和源码都涉及到asn1编解码,比如数字证书申请、数字证书、 crl、ocsp、 pkcs7、 pkcs8、 pkcs12等。


5)查找资料

  • Linux下主要用man就能查看OpenSSL命令和函数的帮助。
  • Windows用户可用到www.openss.org去查看在线帮助文档,或者用linux下的命令man2html将帮助文档装换为html格式。
  • 用户也可以访问OpenSSL.cn论坛来学习OpenSSL。


二、OpenSSL常用功能

OpenSSL的功能非常丰富,本文主要介绍常用的功能,包括:哈希表、BIO、BASE64编解码、摘要算法(MD4、MD5、SHA1)、公钥算法RSA等。


2.1 哈希表

假设有10亿条数据:
(1)如何快速查找一个数据是否存在?–bitmap
(2)如何存储,快速查找是否存在?–布隆过滤器+哈希表


2.1.1 定义

在一般的数据结构如线性表和树中,记录在结构中的相对位置是与记录的关键字之间不存在确定的关系,在结构中查找记录时需进行一系列的关键字比较。这一类查找方法建立在“比较”的基础上,查找的效率与比较次数密切相关。理想的情况是能直接找到需要的记录,因此必须在记录的存储位置和它的关键字之间建立确定的对应关系,使每个关键字和结构中一个唯一的存储位置相对应。在查找时,只需根据这个对应关系找到给定值。这种对应关系既是哈希函数,按这个思想建立的表为哈希表。哈希表存在冲突现象:不同的关键字可能得到同一哈希地址。在建造哈希表时不仅要设定一个好的哈希函数,而且要设定一种处理冲突的方法。


2.1.2 数据结构

OpenSSL函数使用哈希表来加快查询操作,并能存放任意形式的数据,比如配置文件的读取、内存分配中被分配内存的信息等。其源码在crypto/lhash目录下。
OpenSSL中的哈希表数据结构在lhash_local.h中定义如下:

struct lhash_node_st {
   
   
    void *data;  					//用于存放数据地址
    struct lhash_node_st *next; 	//下一个数据地址
    unsigned long hash;				//数据哈希计算值
};

struct lhash_st {
   
   
    OPENSSL_LH_NODE **b;			//指针数组用于存放所有的数据,数组中的每一个值为数据链表的头指针
    OPENSSL_LH_COMPFUNC comp;		//用于存放数据比较函数地址
    OPENSSL_LH_HASHFUNC hash;		//用于存放计算哈希值函数的地址
    unsigned int num_nodes;			//链表个数
    unsigned int num_alloc_nodes;	//为b分配空间的大小
    unsigned int p;
    unsigned int pmax;
    unsigned long up_load;      /* load times 256 */
    unsigned long down_load;    /* load times 256 */
    unsigned long num_items;
    unsigned long num_expands;
    unsigned long num_expand_reallocs;
    unsigned long num_contracts;
    unsigned long num_contract_reallocs;
    TSAN_QUALIFIER unsigned long num_hash_calls;
    TSAN_QUALIFIER unsigned long num_comp_calls;
    unsigned long num_insert;
    unsigned long num_replace;
    unsigned long num_delete;
    unsigned long num_no_delete;
    TSAN_QUALIFIER unsigned long num_retrieve;
    TSAN_QUALIFIER unsigned long num_retrieve_miss;
    TSAN_QUALIFIER unsigned long num_hash_comps;
    int error;
};

基本结构如下示图:
在这里插入图片描述


2.1.3 函数说明

1)创建hash
LHASH *lh_new(LHASH_HASH_FN_TYPE h, LHASH_COMP_FN_TYPE c)

功能:生成hash表
说明:输入参数h为哈希函数, c为比较函数。这两个函数都是回调函数。
因为哈希表用于存放任意的数据结构,哈希表存放、查询、删除等操作都需要比较数据和进行哈希运算,而哈希表不知道用户数据如何进行比较,也不知道用户数据结构中需要对哪些关键项进行散列运算。所以,用户必须提供这两个回调函数。


2)往哈希表中添加数据
void *lh_insert(LHASH *lh, void *data)

功能:往哈希表中添加数据。
说明:data为需要添加数据结构的指针地址。


3)遍历哈希数据
void lh_doall(LHASH *lh, LHASH_DOALL_FN_TYPE func)

功能:处理哈希表中的所有数据。
说明:func为外部提供的回调函数,本函数遍历所有存储在哈希表中的数据,每个数据被func处理。


4)检索
void *lh_retrieve(LHASH *lh, const void *data)

功能:查询数据。
说明:从哈希表中查询数据,data为i数据结构地址,此数据结构中必须提供关键项(这些关键项对应用户提供的哈希函数和比较函数)以供查询,如果查询成功,返回数据结构的地址,否则返回NULL。比如SSL握手中服务端查询以前存储的SESSION时,它需要提供其中关键的几项:

SSL_SESSION *ret=NULL,data;
data.ssl_version=s->version;
data.session_id_length=len;
memcpy(data.session_id,session_id,len);
ret=(SSL_SESSION *)lh_retrieve(s->ctx->sessions,&data);


5)删除
void *lh_delete(LHASH *lh, const void *data)

功能:删除散列表中的一个数据。
说明:data为数据结构指针。


6)释放
void lh_free(LHASH *lh)

功能:释放哈希表。


2.1.4 编程示例
#include <stdio.h>
#include <openssl/lhash.h>


#define NAME_LENGTH		32

typedef struct _Person {
   
   

	char name[NAME_LENGTH];
	int high;
	char otherInfo[NAME_LENGTH];

} Person;


static int person_cmp(const void *a, const void *b) {
   
   

	char *namea = ((Person*)a)->name;
	char *nameb = ((Person*)b)->name;

	return strcmp(namea, nameb);
}

void print_value(void *a) {
   
   
	Person *p = (Person*)a;

	printf("name: %s\n", p->name);
	printf("high: %d\n", p->high);
	printf("other info : %s\n", p->otherInfo);
}

int main() {
   
   

	_LHASH *h = lh_new(NULL, person_cmp);
	if (h == NULL) {
   
   
		printf("err.\n");
		return -1;
	}

	Person p1 = {
   
   "Zhangsan", 170, "xxxx"};
	Person p2 = {
   
   "Lisi", 175, "xxxx"};
	Person p3 = {
   
   "Wangwu", 170, "xxxx"};
	Person p4 = {
   
   "Zhaoliu", 170, "xxxx"};
	
	lh_insert(h, &p1);
	lh_insert(h, &p2);
	lh_insert(h, &p3);
	lh_insert(h, &p4);

	lh_doall(h, print_value);

	printf("\n\n\n------------------------------\n\n\n");
	
	void *data = lh_retrieve(h, (const char *)"Zhangsan");
	if (data == NULL) {
   
   
		return -1;
	}

	print_value(data);

	lh_free(h);

	return 0;

}


2.2 BIO

2.2.1 定义

OpenSSL抽象IO(I/O abstraction,即BIO)是OpenSSL对于io类型的抽象封装,包括:内存、文件、日志、标准输入输出、 socket( TCP/UDP)、加/解密、摘要和ssl通道等。
OpenSSL BIO通过回调函数为用户隐藏了底层实现细节,所有类型的bio的调用大体上是类似的。 BIO中的数据能从一个BIO传送到另外一个BIO或者是应用程序。


2.2.2 数据结构

BIO数据结构主要有2个,在crypto/bio.h(不同版本位置可能有差异)中定义如下:
1)BIO_METHOD

typedef struct bio_method_st{
   
   
	int type;											//具体BIO类型
	const char *name;									//具体BIO名字
	int (*bwrite)(BIO *, const char *, int);			//具体BIO写操作回调函数
	int (*bread)(BIO *, char *, int);					//具体BIO读操作回调函数
	int (*bputs)(BIO *, const char *);					//具体BIO中写入字符串回调函数
	int (*bgets)(BIO *, char *, int);					//具体BIO中读取字符串函数
	long (*ctrl)(BIO *, int, long, void *);				//具体BIO的控制回调函数
	int (*create
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值