一、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目录下。包括如下内容:

-
asn1 DER编码解码(crypto/asn1目录),包含了基本asn1对象的编解码以及数字证书请求、数字证书、 CRL撤销列表以及PKCS8等最基本的编解码函数。这些函数主要通过宏来实现。
-
抽象IO(BIO,crypto/bio目录),本目录下的函数对各种输入输出进行抽象,包括文件、内存、标准输入输出、 socket和SSL协议等。
-
大数运算(crypto/bn目录),本目录下的文件实现了各种大数运算。这些大数运算主要用于非对称算法中密钥生成以及各种加解密操作。另外还为用户提供了大量辅助函数,比如内存与大数之间的相互转换。
-
字符缓存操作(crypto/buffer目录)
-
配置文件读取(crypto/conf目录), OpenSSL主要的配置文件为OpenSSL.cnf。本目录下的函数实现了对这种格式配置文件的读取操作。
-
DSO(动态共享对象,crypto/dso目录),本目录下的文件主要抽象了各种平台的动态库加载函数,为用户提供统一接口。
-
硬件引擎(crypto/engine目录),硬件引擎接口。用户如果要写自己的硬件引擎,必须实现它所规定的接口。
-
错误处理(crypto/err目录),当程序出现错误时, OpenSSL能以堆栈的形式显示各个错误。本目录下只有基本的错误处理接口,具体的的错误信息由各个模块提供。各个模块专门用于错误处理的文件一般为*_err…c文件。
-
对称算法、非对称算法及摘要算法封装(crypto/evp目录)。
-
HMAC(crypto/hmac目录),实现了基于对称算法的MAC。
-
hash表(crypto/lhash目录),实现了散列表数据结构。 OpenSSL中很多数据结构都是以散列表来存放的。比如配置信息、 ssl session和asn.1对象信息等。
-
数字证书在线认证(crypto/ocsp目录),实现了ocsp协议的编解码以及证书有效性计算等功能。
-
PEM文件格式处理(crypto/pem),用于生成和读取各种PEM格式文件,包括各种密钥、数字证书请求、数字证书、 PKCS7消息和PKCS8消息等。
-
pkcs7消息语法(crypto/pkcs7目录),主要实现了构造和解析PKCS7消息;
-
pkcs12个人证书格式(crypto/pckcs12目录),主要实现了pkcs12证书的构造和解析。
-
队列(crypto/pqueue目录),实现了队列数据结构,主要用于DTLS。
-
随机数(crypto/rand目录),实现了伪随机数生成,支持用户自定义随机数生成。
-
堆栈(crypto/stack目录),实现了堆栈数据结构。
-
线程支持(crypto/threads), OpenSSL支持多线程,但是用户必须实现相关接口。
-
文本数据库(crypto/txt_db目录)。
-
x509数字证书(crypto/x509目录和crypto/x509v3),包括数字证书申请、数字证书和CRL的构造、解析和签名验证等功能;
-
对称算法(crypto/aes、 crypto/bf、 crypto/cast、 ccrypto/omp和crypto/des等目录)。
-
非对称算法(crypto/dh、 crypto/dsa、 crypto/ec和crypto/ecdh)。
-
摘要算法(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

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

被折叠的 条评论
为什么被折叠?



