Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
ART-Pi
SSL/TLS
TLS 如何在 sal 接口上实现
发布于 2021-08-08 21:47:47 浏览:2849
订阅该版
[tocm] # 1. TLS的背景 在SSL/TLS出现之前,很多应用层协议(http、ftp、smtp等)都存在着网络安全问题,例如大家所熟知的http协议,在传输过程中使用的是明文信息,传输报文一旦被截获便会泄露传输内容;传输过程中报文如果被篡改,无法轻易发现;无法保证消息交换的对端身份的可靠性。为了解决此类问题,人们在应用层和传输层之间加入了SSL/TLS协议。 如下图所示,HTTP/1.1 协议默认是以明文方式传输数据的,这就带来三个风险:窃听风险、伪装风险、篡改风险。HTTP 协议自身没有加密机制,但可以通过和 TLS (Transport Layer Security) / SSL (Secure Socket Layer) 的组合使用,加密 HTTP 的通信内容,借助 TLS / SSL 提供的信息加密功能、完整性校验功能、身份验证功能保障网络通信的安全,与 TLS / SSL 组合使用的 HTTP 被称为 HTTPS(HTTPSecure),可以说 HTTPS 相当于身披 SSL / TLS 外壳的 HTTP。 ![53a30392e65b9b6cc1ef5c660951791a.png.webp](https://oss-club.rt-thread.org/uploads/20210808/53a30392e65b9b6cc1ef5c660951791a.png.webp) HTTPS协议分层模型 ## # 2. 什么是TLS TLS(Transport Layer Security,安全传输层),TLS是建立在传输层TCP协议之上的协议,服务于应用层,它的前身是SSL(Secure Socket Layer,安全套接字层),它实现了将应用层的报文进行加密后再交由TCP进行传输的功能。 ## 2.1 TLS的作用 TLS协议主要解决如下三个网络安全问题。 保密(message privacy),保密通过加密encryption实现,所有信息都加密传输,第三方无法嗅探; 完整性(message integrity),通过MAC校验机制,一旦被篡改,通信双方会立刻发现; 认证(mutual authentication),双方认证,双方都可以配备证书,防止身份被冒充; ## 2.2 TLS的发展过程 1995: SSL 2.0, 由Netscape提出,这个版本由于设计缺陷,并不安全,很快被发现有严重漏洞,已经废弃。 1996: SSL 3.0. 写成RFC,开始流行。目前(2015年)已经不安全,必须禁用。 1999: TLS 1.0. 互联网标准化组织ISOC接替NetScape公司,发布了SSL的升级版TLS 1.0版 2006: TLS 1.1. 作为 RFC 4346 发布。主要修复了CBC模式相关的如BEAST攻击等漏洞 2008: TLS 1.2. 作为 RFC 5246 发布 。增进安全性,目前应该主要部署的版本 2018: TLS 1.3 作为 RFC 8446 发布 ,大幅增进安全性 ![911e4bcb19665c62d66cf0bb610cb709.png.webp](https://oss-club.rt-thread.org/uploads/20210808/911e4bcb19665c62d66cf0bb610cb709.png.webp) SSL/TLS协议发布时间与网站支持率对比 ## 2.3 TLS 报文结构 TLS (Transport Layer Security)协议是由TLS 记录协议(TLS Record Protocol)和TLS 握手协议(TLS Handshake Protocol)这两层协议叠加而成的,位于底层的TLS 记录协议负责进行信息传输和认证加密,位于上层的TLS 握手协议则负责除加密以外的其它各种操作,比如密钥协商交换等。上层的TLS 握手协议又可以分为4个子协议,TLS 协议的层次结构如下图所示: ![1df5174d428d9c1e0b99cf2f56b4b2e2.png.webp](https://oss-club.rt-thread.org/uploads/20210808/1df5174d428d9c1e0b99cf2f56b4b2e2.png.webp) TLS协议的层次结构 前面已经介绍了TLS 协议的认证加密算法,这里先介绍TLS 记录协议的报文结构,每一条TLS 记录以一个短标头起始,标头包含记录内容的类型(或子协议)、协议版本和长度,消息数据紧跟在标头之后,如下图所示: ![56c532f7a17110e908400686275532c5.png](https://oss-club.rt-thread.org/uploads/20210808/56c532f7a17110e908400686275532c5.png) TLS记录报文 了解了TLS 记录报文结构,前面也介绍了认证加密过程,下面以AES-GCM为例,看看密文(消息明文AES-CNT加密而来,当不需要对消息加密时,此处保持明文即可)和MAC(将序列号、标头等附加信息与密文一起通过Galois-MAC计算而来)是如何添加进TLS 记录报文的: 上图中的几个字段作用如下: 序列号:任一端都有自身的序列号并跟踪来自另一端TLS记录的数量,能确保消息不被重放攻击; 标头:将标头作为计算GMAC输入的一部分,能确保未进行加密的标头不会遭受篡改; nonce:在加密通信中仅使用一次的密钥,比如前面介绍过的 IV(Initialization Vector)或CTR初始值。 # 3. RT-Thread使用TLS 目前常用的 TLS 方式:MbedTLS、OpenSSL、s2n 等,但是对于不同的加密方式,需要使用其指定的加密接口和流程进行加密,对于部分应用层协议的移植较为复杂。因此 SAL TLS 功能产生,主要作用是提供 Socket 层面的 TLS 加密传输特性,抽象多种 TLS 处理方式,提供统一的接口用于完成 TLS 数据交互。 ## 3.1 RT-Thread SAL TLS 功能使用方式 使用流程如下: 配置开启任意网络协议栈支持(如 lwIP 协议栈); ![f087c7f0893e482405663268f5915a31.png](https://oss-club.rt-thread.org/uploads/20210808/f087c7f0893e482405663268f5915a31.png) 配置开启 MbedTLS 软件包(目前RT-Thread只支持 MbedTLS 类型加密方式); ![截图.png](https://oss-club.rt-thread.org/uploads/20210808/796b37fe86adcba9fe3185472769cc50.png) 配置开启 SAL_TLS 功能支持(如下配置选项章节所示); ![image.png](https://oss-club.rt-thread.org/uploads/20210808/d40bae7422dfec52baed54eedae3cb66.png) 配置完成之后,只要在 socket 创建时传入的 protocol 类型使用 PROTOCOL_TLS 或 **PROTOCOL_DTLS **,即可使用标准 BSD Socket API 接口,完成 TLS 连接的建立和数据的收发。示例代码如下所示: ```c #include
#include
#include
#include
#include
/* RT-Thread 官网,支持 TLS 功能 */ #define SAL_TLS_HOST "www.rt-thread.org" #define SAL_TLS_PORT 443 #define SAL_TLS_BUFSZ 1024 static const char *send_data = "GET /download/rt-thread.txt HTTP/1.1\r\n" "Host: www.rt-thread.org\r\n" "User-Agent: rtthread/4.0.1 rtt\r\n\r\n"; void sal_tls_test(void) { int ret, i; char *recv_data; struct hostent *host; int sock = -1, bytes_received; struct sockaddr_in server_addr; /* 通过函数入口参数url获得host地址(如果是域名,会做域名解析) */ host = gethostbyname(SAL_TLS_HOST); recv_data = rt_calloc(1, SAL_TLS_BUFSZ); if (recv_data == RT_NULL) { rt_kprintf("No memory\n"); return; } /* 创建一个socket,类型是SOCKET_STREAM,TCP 协议, TLS 类型 */ if ((sock = socket(AF_INET, SOCK_STREAM, PROTOCOL_TLS)) < 0) { rt_kprintf("Socket error\n"); goto __exit; } /* 初始化预连接的服务端地址 */ server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SAL_TLS_PORT); server_addr.sin_addr = *((struct in_addr *)host->h_addr); rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero)); if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0) { rt_kprintf("Connect fail!\n"); goto __exit; } /* 发送数据到 socket 连接 */ ret = send(sock, send_data, strlen(send_data), 0); if (ret <= 0) { rt_kprintf("send error,close the socket.\n"); goto __exit; } /* 接收并打印响应的数据,使用加密数据传输 */ bytes_received = recv(sock, recv_data, SAL_TLS_BUFSZ - 1, 0); if (bytes_received <= 0) { rt_kprintf("received error,close the socket.\n"); goto __exit; } rt_kprintf("recv data:\n"); for (i = 0; i < bytes_received; i++) { rt_kprintf("%c", recv_data[i]); } __exit: if (recv_data) rt_free(recv_data); if (sock >= 0) closesocket(sock); } #ifdef FINSH_USING_MSH #include
MSH_CMD_EXPORT(sal_tls_test, SAL TLS function test); #endif /* FINSH_USING_MSH */ ``` # 4. RT-Thread 基于SAL接口的TLS实现 ## 4.1 TLS接口注册 TLS(Transport Layer Security)安全传输层协议主要用于通信数据的加密,并不影响SAL向上提供的接口。RT-Thread使用的TLS组件时mbedtls(一个由 ARM 公司使用 C 语言实现和维护的 SSL/TLS 算法库),如果启用了TLS组件,SAL层的实现函数中会自动调用mbedtls的接口函数,实现数据的加密传输。 RT-Thread的SAL接口的TLS协议的数据结构描述与需要向其注册的接口函数集合如下: ```c // .\rt-thread\components\net\sal_socket\include\sal_tls.h struct sal_proto_tls { char name[RT_NAME_MAX]; /* TLS protocol name */ const struct sal_proto_tls_ops *ops; /* SAL TLS protocol options */ }; struct sal_proto_tls_ops { int (*init)(void); void* (*socket)(int socket); int (*connect)(void *sock); int (*send)(void *sock, const void *data, size_t size); int (*recv)(void *sock, void *mem, size_t len); int (*closesocket)(void *sock); int (*set_cret_list)(void *sock, const void *cert, size_t size); /* Set TLS credentials */ int (*set_ciphersurite)(void *sock, const void* ciphersurite, size_t size); /* Set select ciphersuites */ int (*set_peer_verify)(void *sock, const void* peer_verify, size_t size); /* Set peer verification */ int (*set_dtls_role)(void *sock, const void *dtls_role, size_t size); /* Set role for DTLS */ }; ``` sal_proto_tls对象的初始化和注册过程如下,(注册相关函数代码位于rt-thread\components\net\sal_socket\impl\proto_mbedtls.c): ```c // .\rt-thread\components\net\sal_socket\impl\proto_mbedtls.c static const struct sal_proto_tls_ops mbedtls_proto_ops= { RT_NULL, mebdtls_socket, mbedtls_connect, (int (*)(void *sock, const void *data, size_t size)) mbedtls_client_write, (int (*)(void *sock, void *mem, size_t len)) mbedtls_client_read, mbedtls_closesocket, }; static const struct sal_proto_tls mbedtls_proto = { "mbedtls", &mbedtls_proto_ops, }; int sal_mbedtls_proto_init(void) { /* register MbedTLS protocol options to SAL */ sal_proto_tls_register(&mbedtls_proto); return 0; } INIT_COMPONENT_EXPORT(sal_mbedtls_proto_init); // .\rt-thread\components\net\sal_socket\src\sal_socket.c #ifdef SAL_USING_TLS /* The global TLS protocol options */ static struct sal_proto_tls *proto_tls; #endif #ifdef SAL_USING_TLS int sal_proto_tls_register(const struct sal_proto_tls *pt) { RT_ASSERT(pt); proto_tls = (struct sal_proto_tls *) pt; return 0; } #endif ``` 变量proto_tls在文件sal_socket.c中属于全局变量(被static修饰,仅限于本文件内),该文件是SAL组件对外访问接口函数的实现文件。也就是说,我们启用TLS协议后,SAL组件对外提供的访问接口函数实现代码中就会调用TLS接口,完成数据的加密传输,而且该组件的注册是被自动初始化的,比较省心。 ## 4.2 SAL对外提供的访问接口 SAL组件初始化并注册成功后,我们就可以使用SAL提供的接口进行应用开发了,先看看SAL组件提供了哪些访问接口: ```c // .\rt-thread\components\net\sal_socket\include\sal_socket.h int sal_accept(int socket, struct sockaddr *addr, socklen_t *addrlen); int sal_bind(int socket, const struct sockaddr *name, socklen_t namelen); int sal_shutdown(int socket, int how); int sal_getpeername (int socket, struct sockaddr *name, socklen_t *namelen); int sal_getsockname (int socket, struct sockaddr *name, socklen_t *namelen); int sal_getsockopt (int socket, int level, int optname, void *optval, socklen_t *optlen); int sal_setsockopt (int socket, int level, int optname, const void *optval, socklen_t optlen); int sal_connect(int socket, const struct sockaddr *name, socklen_t namelen); int sal_listen(int socket, int backlog); int sal_recvfrom(int socket, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); int sal_sendto(int socket, const void *dataptr, size_t size, int flags, const struct sockaddr *to, socklen_t tolen); int sal_socket(int domain, int type, int protocol); int sal_closesocket(int socket); int sal_ioctlsocket(int socket, long cmd, void *arg); ``` ```c // .\rt-thread\components\net\sal_socket\include\sal_netdb.h struct hostent *sal_gethostbyname(const char *name); int sal_gethostbyname_r(const char *name, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop); void sal_freeaddrinfo(struct addrinfo *ai); int sal_getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res); ``` 如果不习惯使用SAL层提供的sal_xxx形式的接口,SAL还为我们进行了再次封装,将其封装为比较通用的BSD Socket API,封装后的接口如下: ```c // .\rt-thread\components\net\sal_socket\include\socket\sys_socket\sys\socket.h #ifdef SAL_USING_POSIX int accept(int s, struct sockaddr *addr, socklen_t *addrlen); int bind(int s, const struct sockaddr *name, socklen_t namelen); int shutdown(int s, int how); int getpeername(int s, struct sockaddr *name, socklen_t *namelen); int getsockname(int s, struct sockaddr *name, socklen_t *namelen); int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen); int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen); int connect(int s, const struct sockaddr *name, socklen_t namelen); int listen(int s, int backlog); int recv(int s, void *mem, size_t len, int flags); int recvfrom(int s, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); int send(int s, const void *dataptr, size_t size, int flags); int sendto(int s, const void *dataptr, size_t size, int flags, const struct sockaddr *to, socklen_t tolen); int socket(int domain, int type, int protocol); int closesocket(int s); int ioctlsocket(int s, long cmd, void *arg); #else #define accept(s, addr, addrlen) sal_accept(s, addr, addrlen) #define bind(s, name, namelen) sal_bind(s, name, namelen) #define shutdown(s, how) sal_shutdown(s, how) #define getpeername(s, name, namelen) sal_getpeername(s, name, namelen) #define getsockname(s, name, namelen) sal_getsockname(s, name, namelen) #define getsockopt(s, level, optname, optval, optlen) sal_getsockopt(s, level, optname, optval, optlen) #define setsockopt(s, level, optname, optval, optlen) sal_setsockopt(s, level, optname, optval, optlen) #define connect(s, name, namelen) sal_connect(s, name, namelen) #define listen(s, backlog) sal_listen(s, backlog) #define recv(s, mem, len, flags) sal_recvfrom(s, mem, len, flags, NULL, NULL) #define recvfrom(s, mem, len, flags, from, fromlen) sal_recvfrom(s, mem, len, flags, from, fromlen) #define send(s, dataptr, size, flags) sal_sendto(s, dataptr, size, flags, NULL, NULL) #define sendto(s, dataptr, size, flags, to, tolen) sal_sendto(s, dataptr, size, flags, to, tolen) #define socket(domain, type, protocol) sal_socket(domain, type, protocol) #define closesocket(s) sal_closesocket(s) #define ioctlsocket(s, cmd, arg) sal_ioctlsocket(s, cmd, arg) #endif /* SAL_USING_POSIX */ ``` ```c // .\rt-thread\components\net\sal_socket\include\socket\netdb.h struct hostent *gethostbyname(const char *name); int gethostbyname_r(const char *name, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop); void freeaddrinfo(struct addrinfo *ai); int getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res); ``` BSD Socket API也是我们进行网络应用开发时最常使用的接口,如果要使用这些接口,除了启用相应的宏定义,还需要包含这些接口所在的两个头文件。 # 5. RT-Thread TLS拓展 ## 5.1 Wireshark 抓取TLS报文 ### 5.1.1 配置抓包环境 1.SG105 Pro网关交换机配置 ![2021-08-08_211013.png](https://oss-club.rt-thread.org/uploads/20210808/d07da8c21843dc10818d6d0ed95583ec.png) 按照图片所示配置配置网关交换机的端口镜像功能,这样端口1连接PC,端口2连接开发板,开发板所有数据包都可以通过PC端的wireshark软件抓取。 2.PC端Wireshark配置 ![image.png](https://oss-club.rt-thread.org/uploads/20210808/a0d448abb0276bb8fa836dacab445065.png) 设置IP地址过滤(ip.addr == xxx.xxx.xxx.xxx,其中ip地址为开发板ip地址) 3.webclient抓包http协议,所有数据都是明文,没有加密 ```c #include
#include
#define GET_HEADER_BUFSZ 1024 #define GET_RESP_BUFSZ 1024 #define GET_LOCAL_URI "http://www.rt-thread.com/service/rt-thread.txt" /* send HTTP GET request by common request interface, it used to receive longer data */ static int webclient_get_comm(const char *uri) ``` ![image.png](https://oss-club.rt-thread.org/uploads/20210808/d018618083ad74bbdb52c8b76209f091.png) ![image.png](https://oss-club.rt-thread.org/uploads/20210808/abab37b84db91c4edf91d4d8b9e9f684.png) ![image.png](https://oss-club.rt-thread.org/uploads/20210808/21b92e0698cf7ba0d56184546611cc9b.png) 3.webclient抓包https协议,密文数据 ```c #include
#include
#define GET_HEADER_BUFSZ 1024 #define GET_RESP_BUFSZ 1024 #define GET_LOCAL_URI "https://www.rt-thread.com/service/rt-thread.txt" /* send HTTP GET request by common request interface, it used to receive longer data */ static int webclient_get_comm(const char *uri) ``` ![image.png](https://oss-club.rt-thread.org/uploads/20210808/ed6735cebcb7baf402e1ed05349120c7.png) ![image.png](https://oss-club.rt-thread.org/uploads/20210808/fb1f58e140d5bedd7b887f1eacbdf025.png) ![image.png](https://oss-club.rt-thread.org/uploads/20210808/1bd7fd110da15ee121f73daafb48ece3.png) # 6. 参考文献: 1.[Web技术(三):TLS 1.2/1.3 加密原理(AES-GCM + ECDHE-ECDSA/RSA)_流云-CSDN博客_tls加密原理](https://blog.csdn.net/m0_37621078/article/details/106028622) 2.[TLS详解](https://www.jianshu.com/p/1fc7130eb2c2) 3.[TLS_百度百科](https://baike.baidu.com/item/TLS/2979545) 4.[SAL套接字抽象层](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/sal/sal) 5.[IOT-OS之RT-Thread(十三)--- 网络分层结构 + netdev/SAL原理_流云-CSDN博客](https://blog.csdn.net/m0_37621078/article/details/104836942?spm=1001.2014.3001.5501)
5
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
windoufu
这家伙很懒,什么也没写!
文章
4
回答
3
被采纳
0
关注TA
发私信
相关文章
1
SAL组件好像并不支持DTLS
2
阿里云物模型例程握手失败
3
socket使用SAL TLS没有反应
4
有没有小一点的TLS库
5
pahomqtt怎么配置ssl单向认证?
6
air724+阿里云OTA升级失败,SSL报错code = -29056
7
SSL连接会进入死循环
8
mqtt 通过ssl加密传输
9
阿里云OTA升级过程中,握手失败。
10
不能连接网络因为SSL模式不可用
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
MicroPython
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
15
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
13
个答案
1
次被采纳
本月文章贡献
程序员阿伟
9
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
5
次点赞
RTT_逍遥
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部