基于CyaSSL库的TLS实现

您所在的位置:网站首页 tls协议过程 基于CyaSSL库的TLS实现

基于CyaSSL库的TLS实现

2023-03-22 14:42| 来源: 网络整理| 查看: 265

声明

此文章位平时测试TLS是的随笔,使用老版本CyaSSL库,新版本已经更名为WolfSSL,但是整体使用逻辑没变,可做参考。

文章内容有错误或遗漏,欢迎指正及补充及指正。文章内容部分源于网络,若有侵权联系删除。

SSL协议结构

比较清晰的一张结构图,可以看到SSL就是基于TCP之上,应用层之下的一个补充协议,再CyaSSL库的使用中也可以看出这个结构的雏形。应用层调用库里的接口,SSL再初始化的时候与已经建立的TCP连接建立绑定,相较之前就是在应用层与传输层间做了个数据的处理。

简述SSL连接

我们知道无论任何协议的交互过程,其实都是一个数据交换的过程,大体流程都是基本一致,主动发起端(一般是客户端)向被动接收端(一般是服务器)发送本机的相关数据,接收端收到后解析数据内容,获取所需要的数据信息后再根据规则返回数据,照此多次进行数据交互后就是一个完成的协议交互过程。所以我们只需要了解双方在每个过程需要获取到的数据信息就可以完成整个协议的理解。所以此处对建立连接过程不展开解释,只对数据包内所包含的各个信息做简单讲解。

SSL连接建立阶段,该阶段客户端服务器互相交互信息,同步协议版本,交换密钥、信息验证和加密算法及生成的随机数。

ClientHello

客户端版本:

按优先级列出客户端支持的协议版本,首选客户端希望支持的最新协议版本。

客户端随机数:

用以和其他随结束生成master key,后续的对称加密就是使用的master key

会话ID:

客户端第一次连接到服务器,那么这个字段就会保持为空。如果该字段不为空,说明以前是与服务器有连接的,在此期间,服务器将使用Session ID映射对称密钥,并将Session ID存储在客户端浏览器中,为映射设置一个时间限。如果浏览器将来连接到同一台服务器(在时间到期之前),它将发送Session ID,服务器将对映射的Session ID进行验证,并使用以前用过的对称密钥来恢复Session,这种情况下不需要完全握手。也叫作SSL会话恢复。

加密套件:

客户端会给服务器发送自己已经知道的密码套件列表,这是由客户按优先级排列的,但完全由服务器来决定发送与否。TLS中使用的密码套件有一种标准格式。上面的报文中,客户端发送了74套加密套件。服务端会从中选出一种来作为双方共同的加密套件。

压缩方法:

客户端支持的压缩算法列表

ServerHello

服务器版本

服务器会选择客户端支持的最新版本。

服务器随机数:

服务器和客户端都会生成32字节的随机数。用来创建加密密钥。

加密套件:

服务器会从客户端发送的加密套件列表中选出一个加密套件。

会话ID:

服务器将约定的Session参数存储在TLS缓存中,并生成与其对应的Session id。它与Server Hello一起发送到客户端。客户端可以写入约定的参数到此Session id,并给定到期时间。客户端将在Client Hello中包含此id。如果客户端在此到期时间之前再次连接到服务器,则服务器可以检查与Session id对应的缓存参数,并重用它们而无需完全握手。

压缩方法:

如果支持,服务器将同意客户端的首选压缩方法。

Certificate(可选):

第一次建立必须要有证书。一般情况下,除了会话恢复时不需要发送该消息,在SSL握手的全流程中,都需要包含该消息。消息包含一个X.509证书,证书中包含公钥,发给客户端用来验证签名或在密钥交换的时候给消息加密。

Server Key Exchange(可选)

根据之前在ClientHello消息中包含的CipherSuite信息,决定了密钥交换方式(例如RSA或者DH),因此在Server Key Exchange消息中便会包含完成密钥交换所需的一系列参数。

Certificate Request(可选)

这一步是可选的,如果在对安全性要求高的常见可能用到。服务器用来验证客户端。服务器端发出Certificate Request消息,要求客户端发他自己的证书过来进行验证。该消息中包含服务器端支持的证书类型(RSA、DSA、ECDSA等)和服务器端所信任的所有证书发行机构的CA列表,客户端会用这些信息来筛选证书。

Server Hello Done

该消息表示服务器已经将所有信息发送完毕,接下来等待客户端的消息。

Certificate

如果在第二阶段服务器端要求发送客户端证书,客户端便会在该阶段将自己的证书发送过去。服务器端在之前发送的Certificate Request消息中包含了服务器端所支持的证书类型和CA列表,因此客户端会在自己的证书中选择满足这两个条件的第一个证书发送过去。若客户端没有证书,则发送一个no_certificate警告。

Client Key Exchange:

根据之前从服务器端收到的随机数,按照不同的密钥交换算法,算出一个pre-master,发送给服务器,服务器端收到pre-master算出main master。而客户端当然也能自己通过pre-master算出main master。如此以来双方就算出了对称密钥。

Certificate verify:

只有在客户端发送了自己证书到服务器端,这个消息才需要发送。其中包含一个签名,对从第一条消息以来的所有握手消息的HMAC值(用master_secret)进行签名。

ChangeCipherSpec

编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送(ChangeCipherSpec是一个独立的协议,体现在数据包中就是一个字节的数据,用于告知服务端,客户端已经切换到之前协商好的加密套件(Cipher Suite)的状态,准备使用之前协商好的加密套件加密数据并传输了)。

Clinet Finished:

客户端握手结束通知, 表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验

Server Finished:

服务端握手结束通知。

连接注意事项

1、确保TCP连接已建立,能够进行基本的TCP数据传输

2、确认双方加密套件支持要有共用的加密套件

3、若要使用证书记得本机设备要有正确的时间信息及证书的时效性都要正确,当然可以直接改底层代码,强行关闭证书时效性验证。

CyaSSL使用过程

使用过程讲解使用MQTTS作为介绍,功能包括MQTT TLS 1.2无认证连接,单向证书认证连接及双向证书及密钥连接。服务其使用emqx自带的8883 SSL服务器,证书使用的emqx自带的证书文件,证书格式转换使用的Linux下的OpenSSL进行证书格式转换及证书内容分析(关于OpenSSL工具使用后续补充)。

1、库初始化CyaSSL——>int CyaSSL_Init(void)

先进行库的初始化,该接口主要目的其实就是在对互斥锁进行初始化,对于不同系统的锁有不同接口对照,在使用CyaSSL自己的接口进行调用。接口内部对ssl.c内的全局变量:initRefCount进行操作,若初始化成功后会对该计数进行自增,这是一个保险措施,后续的所有和初始化有关的操作都会对该计数做出判断,防止出现多次初始化造成的空间浪费,是一个预防措施。

2、创建CyaSSL_CTX——>CYASSL_CTX* CyaSSL_CTX_new(CYASSL_METHOD* method)

虽说整个库都是使用的C语言编写,但是面向对象编程的方式使用更方便,这里其实就有一点偏向与面向对象编程的方式。这个CTX可以理解未一个SSL的对象。将所有的相关参数和接口封再这么一个结构体中,之后的参数调用就可以使用这个CTX对象。在接口内开辟一块空间用以存储相应的参数。对该空间内参数进行结构体初始化,默认参数归零。最后返回地址。注意,因为有空间开辟,所以后续若有过程失败一定要对该指针进行空间释放。

3、SSL协议选择CYASSL_METHOD

就是在创建对象时的传参就是指定接口的返回值,接口内部就是初始化了一个CYASSL_METHOD的对象后进行指定版本的初始化,最后同样返回指针,在CTX_new里将该指针赋到CyaSSL_CTX中,完成CTX的版本参数配置。

证书及密钥加载 CyaSSL_CTX_set_verify(mqtt_ctx, SSL_VERIFY_NONE, NULL)

关闭证书默认情况下证书加载时开启的,若是想要关闭加载证书,使用指定设置证书接口,

第一个参数就是CTX对象,接口内部也是主要对CTX的对应参数进行配置。

CyaSSL_CTX_load_verify_buffer(mqtt_ctx, ssl_cer_data, cert_len, SSL_FILETYPE_PEM);

Ca证书加载使用接口,此处使用的方式时无文件系统方式,既可以使用内存方式存储证书,ssl_cer_data就是证书存储地址,cer_len就是证书长度,SSL_FILETYPE_PEM即证书文件格式。

CyaSSL_CTX_use_certificate_buffer(mqtt_ctx, ssl_cer_data, cert_len, SSL_FILETYPE_PEM);

客户端证书加载,与CA证书加载接口使用方式一样,参数也一样。

CyaSSL_CTX_use_PrivateKey_buffer(mqtt_ctx, ssl_cer_data, cert_len, SSL_FILETYPE_PEM);

客户端密钥加载,整体与前两个函数一致。

此项目中没有文件系统,所以使用的无文件系统模式的证书加载,开启该模式在setting.h中添加配置NO_FILESYSTEM。每个证书加载接口使用后,存放证书的空间可以释放,在ssl_cer_data=NULL后再使用该指针进行下一个证书的读取、存放和加载操作,这对整个证书的加载及使用没有影响。

5、新建SSL描述符CYASSL——>CyaSSL_new(CYASSL_CTX)

创建一个新的SSL描述符,就和TCP一样,new一个套接字描述符,后面的数据收发接口都是通过他来使用的。其传参未前面创建的CTX,我认为是可以这么理解的,前面的CTX其实是在创建一个适于SSL描述符的上下文或者环境,再这个CTX中配置好SSL的版本、支持的加密套件、证书等等。

6、与tcp fd建立连接 CyaSSL_set_fd(CYASSL* ssl, int fd)

前面再SSL简述时说过,这是一个基于TCP的一个协议,再TCP层之上,应用层之下,所以该库并没有添加对应的TCP写一部分,采用已有的TCP描述符与建立好的SSL描述符建立关系,就是把TCP描述符的收发接口赋给SSL描述符去使用,后面在使用SSL描述符进行收发的时候就可以直接调用TCP的接口,这样就只需要直接调用SSL的收发接口就可以了。这里就可以看成TCP为SSL的底层,这样理解可能更容易理解一些。

7、SSL连接建立 CyaSSL_connect(CYASSL* ssl)

与获取完TCP描述符后,SSL就正式拥有了网络收发的搜友功能,可以开始SSL的连接过程。使用CyaSSL_connect调用CYASSL描述符连接远端服务器,具体连接过程不做讲述,其函数内部的大致逻辑就是根据一个CYASSL的状态标志位在一个大循环内,根据标志位状态去做特定的动作和阶段,内部也有对端返还数据解析部分,整体过程与前面讲述的连接过程所差无异。

8、SSL数据接收 int CyaSSL_read(CYASSL* ssl, void* data, int sz)

接口内部主要是对于数据的解析,根据前面的CTX内参数,这个内部参数在前面连接的过程中也有所修改,因为要和对端确定传输协议的很多参数。接口返回数据获取长度,指针内存放数据。

9、SSL数据发送 int CyaSSL_write(CYASSL* ssl, const void* data, int sz)

与接收接口的参数基本一致,SSL描述符、数据地址、数据长度。内部逻辑主要是检测连接状态,检查数据最大长度限制,建立数据包,数据发送,返回发送数据数量。

10、关闭连接

1.CyaSSL_shutdown,向对端发送Alert,主动关闭整个连接

2.CyaSSL_free,CYASSL描述符空间释放,这里一定要释放,否则若是长时间连接不上可能会造成空间异常等问题。

3.CyaSSL_CTX_free,CTX空间释放,这个也是必不可少的。

11、开启底层LOG

1.Setting开启CYASSL_DEBUG宏配置。

2.CyaSSL_Debugging_ON();调用接口开启LOG,这里面就是开启一个标志位。

3.CyaSSL_SetLoggingCb(CyaSSL_Logging_cb f)设置自己的LOG接口,

4.也可以直接修改cyassl_log中的log输出接口,这样比较方便就是不太合规。

CyaSSL使用注意

1、做好系统配置,若是没有对应的系统配置的话就需要自己添加,默认配置系统位Windows,自己添加系统配置。

2、注意内存泄露问题,很多底层有开辟空间的地方,很多接口返回地址的就有一定概率会有。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3