WIZnet W55MH32 的 TLS 硬件加密功能说明
W55MH32是一款由WIZnet设计的增强型32位微控制器,其特性非常适合安全的网络应用 。它拥有216MHz的处理器核心,以及高达1024KB的Flash内存和96KB的SRAM 。 至关重要的是,它包含了用于关键加密操作的专用硬件,例如内置了DES、AES和SHA算法的。
简介
硬件加密算法单元,以及一个真随机数发生器(TRNG),这些硬件特性对于实现高性能和安全的TLS 1.2至关重要,因为它们能将计算密集型任务从主CPU上卸载 。 W55MH32还支持硬连线TCP/IP协议栈和10/100M以太网MAC和PHY,使其成为一个强大的安全互联网通信平台 。
什么是TLS?
传输层安全(TLS)是一种加密协议,旨在通过计算机网络提供安全的通信。它确保三个关键的安全属性:
- 机密性(Confidentiality): 它对数据进行加密,以防止窃听和未经授权的访问。
- 完整性(Integrity): 它使用消息认证码(MAC)来确保数据在传输过程中没有被篡改。
- 真实性(Authenticity): 它使用数字证书和签名来验证服务器(以及可选的客户端)的身份。
TLS 是从 SSL(安全套接层)演变而来的,是用于保护网络流量(HTTPS)和其他数据通信的当前标准。它是现代互联网安全的一个基本组成部分。
TLS 握手和密钥交换
W55MH32 上的 TLS 1.2 握手利用芯片的硬件有效地建立安全会话。

Client Hello (客户端问候)
客户端发送一个“Client Hello”消息,其中包括:
1.支持的TLS版本 。
2.支持的密码套件和压缩方法列表 。
3.一个32字节的客户端随机数 。这个随机数是使用W55MH32的TRNG功能生成的,该功能有四个独立的真随机源, 可以一次性生成128位的随机数 。这确保了随机数的质量对于加密目的来说是最高的 。

Server Hello (服务器问候)
服务器发送一个“Server Hello”消息,其中包括:
1.选择的TLS版本 。
2.选择的密码套件(例如,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384)和压缩方法 。
3.一个32字节的服务器随机数 。
4.服务器的数字证书,如果选择DHE密钥交换,还包括服务器的临时DH公钥 。
认证与密钥交换(客户端执行)

客户端接收服务器的消息并对服务器的身份进行认证 。
1.客户端的MCU使用硬件加密算法单元中的W55MH32的SHA硬件来验证服务器证书上的数字签名,这确认了证书的真实性和完整性 。
2.客户端随后使用服务器的临时DH公钥和自己的私钥独立计算一个预主密钥 。
Client Key Exchange (客户端密钥交换)
客户端向服务器发送其密钥交换消息:
DHE:客户端的临时公钥 。
- 客户端使用自己的TRNG功能生成其临时DH公钥并发送给服务器 。
- 服务器随后使用这个公钥以及自己的私钥来计算相同的预主密钥 。
RSA:预主密钥 。
- 客户端生成一个秘密,并使用服务器的公钥安全地将其发送给服务器 。
- 客户端使用W55MH32的TRNG功能生成一个48字节的 预主密钥 。
- 客户端使用证书中的服务器公钥对该预主密钥进行加密 。
- 客户端将加密后的预主密钥发送给服务器 。
- 只有拥有匹配私钥的服务器才能解密它 。
ChangeCipherSpec 和 Finished
这是握手的最后验证阶段 。
1.客户端和服务器根据预主密钥和随机数派生出主密钥和会话密钥 。
2.双方发送一个ChangeCipherSpec消息,表示接下来的所有通信都将加密 。
3.客户端发送一个Finished消息 。此消息使用新的会话密钥进行加密,并包含所有先前握手消息的哈希值 。
4.W55MH32的SHA硬件用于计算这个哈希值 。服务器解密它并验证哈希值,以确认密钥交换成功且握手未被篡改 。
数据通信
加密数据传输:
一旦握手完成,所有应用数据都使用会话密钥进行保护 。
1.应用数据使用硬件加密算法单元中的W55MH32的AES硬件模块进行加密 。
2.使用W55MH32的SHA硬件和会话MAC密钥计算消息认证码(MAC) 。
3.加密后的数据和MAC被发送给另一方 。
4.接收方使用W55MH32的AES硬件解密数据,并使用W55MH32的SHA硬件验证MAC 。
5.验证成功保证了数据的完整性和真实性 。

演示部分
W55MH32的TLS 1.2功能演示将遵循以下步骤:
1.硬件与网络设置:初始化W55MH32的10/100M以太网MAC和PHY以建立物理连接,配置硬件TCP/IP协议栈进行网络通信 。
网络配置初始化,其包含的 MAC 地址、IP 地址、网关、子网掩码、DNS 服务器地址和 DHCP 模式等网络配置参数。
/* network information */
wiz_NetInfo default_net_info = {
.mac = {0x00, 0x08, 0xdc, 0x12, 0x22, 0x12},
.ip = {10, 0, 1, 140},
.gw = {10, 0, 1, 254},
.sn = {255, 255, 255, 0},
.dns = {8, 8, 8, 8},
.dhcp = NETINFO_DHCP
};
定义三个变量ethernet_buf作为网络数据收发缓冲区,ssl_target_ip指定了百度服务器的目标IP地址,tlsContext则用于维护TLS连接的所有状态和配置信息。
uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {0};
uint8_t ssl_target_ip[4] = {182, 61, 244, 181}; // baidu
wiz_tls_context tlsContext;
硬件与网络设置,初始化硬件、加密模块、TRNG、定时器,初始化wiztoe并检查PHY链接,最后初始化网络。
/* hardware initialization */
rcc_clk_config();
delay_init();
console_usart_init(115200);
printf("W55MH32 TLS Example\r\n");
/* hardware crypt initialization */
*(uint32_t *)(0x400210F0) = 0x01;
*(uint32_t *)(0x40016C00) = 0xCDED3526;
*(uint32_t *)(0x40016CCC) = 0x07;
/* hardware TRNG enable */
TRNG_Out(ENABLE);
tim3_init();
/* wiztoe init */
wiz_toe_init();
getSHAR(default_net_info.mac);
wiz_phy_link_check();
network_init(ethernet_buf, &default_net_info);
2.TLS安全连接建立流程
接下来开始执行加密通信的初始化和连接建立过程:首先获取并打印加密库版本信息,然后创建SSL Socket并初始化TLS上下文, 接着通过TCP连接到目标服务器的443端口,最后完成TLS握手协议建立安全连接,并打印消息表示MbedTLS传输测试即将开始。
uint32_t ver = wiz_crypt_version();
printf("WIZnet CARD Secure Test Demo V1.0, secure lib version is V%02x.%02x.%02x.%02x\n", ver >> 24, (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF);
printf("WIZnet CARD Crypt Test V1.0 start......\r\n");
/* open socket */
wiz_tls_socket(&tlsContext, SOCKET_SSL_ID, LOCAL_PORT);
/* tls init */
wiz_tls_init(&tlsContext, 2000, (char *)ssl_target_ip, NULL, NULL, NULL);
/* connect server*/
connect(tlsContext.socket_fd, (uint8_t *)ssl_target_ip, SSL_TARGET_PORT);
/* tls handshake */
wiz_tls_connect(&tlsContext, (char *)ssl_target_ip, SSL_TARGET_PORT);
printf("W55MH32 MbedTLS Transmission test start......\r\n");
3.TLS初始化与握手过程
wiz_tls_init 函数用于初始化TLS客户端连接,参数包括:TLS上下文结构体指针、接收超时时间(毫秒)、 目标服务器域名/IP地址、根证书字符串(验证服务器身份)、客户端证书字符串和私钥字符串(用于客户端认证)。
int wiz_tls_init(wiz_tls_context *tlsContext, uint32_t recv_timeout, char *domain_name, char *root_ca, char *client_cert, char *pkey)
{
int ret = 1;
const char *pers = "ssl_client1";
const char *alpnProtocols[] = {"x-amzn-mqtt-ca", NULL};
#if defined(MBEDTLS_ERROR_C)
char error_buf[100];
#endif
#if defined(MBEDTLS_DEBUG_C)
mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL);
#endif
/*
Initialize session data
*/
#if defined(MBEDTLS_ENTROPY_C)
tlsContext->entropy = malloc(sizeof(mbedtls_entropy_context));
#endif
tlsContext->ctr_drbg = malloc(sizeof(mbedtls_ctr_drbg_context));
tlsContext->ssl = malloc(sizeof(mbedtls_ssl_context));
tlsContext->conf = malloc(sizeof(mbedtls_ssl_config));
tlsContext->cacert = malloc(sizeof(mbedtls_x509_crt));
tlsContext->clicert = malloc(sizeof(mbedtls_x509_crt));
tlsContext->pkey = malloc(sizeof(mbedtls_pk_context));
#if defined(MBEDTLS_ENTROPY_C)
mbedtls_entropy_init(tlsContext->entropy);
#endif
mbedtls_ctr_drbg_init(tlsContext->ctr_drbg);
mbedtls_ssl_init(tlsContext->ssl);
mbedtls_ssl_config_init(tlsContext->conf);
mbedtls_x509_crt_init(tlsContext->cacert);
mbedtls_x509_crt_init(tlsContext->clicert);
mbedtls_pk_init(tlsContext->pkey);
const int *ciphersuite_list = mbedtls_ssl_list_ciphersuites();
while (*ciphersuite_list != 0)
{
const char *name = mbedtls_ssl_get_ciphersuite_name(*ciphersuite_list);
if (name != NULL)
printf("%s\r\n", name);
ciphersuite_list++;
}
/*
Initialize certificates
*/
#if defined(MBEDTLS_ENTROPY_C)
if ((ret = mbedtls_ctr_drbg_seed(tlsContext->ctr_drbg, mbedtls_entropy_func, tlsContext->entropy,
(const unsigned char *)pers, strlen(pers)))
!= 0)
{
printf(" failed\r\n ! mbedtls_ctr_drbg_seed returned -0x%x\r\n", -ret);
return -1;
}
#endif
#if defined(MBEDTLS_DEBUG_C)
mbedtls_ssl_conf_dbg(tlsContext->conf, WIZnetDebugCB, stdout);
#endif
/*
Parse certificate
*/
if (root_ca != NULL)
{
uint16_t root_ca_len = strlen(root_ca);
printf(" Loading the CA root certificate len = %d\r\n", root_ca_len);
ret = mbedtls_x509_crt_parse(tlsContext->cacert, (const unsigned char *)root_ca, root_ca_len + 1);
if (ret < 0)
{
printf(" failed\r\n ! mbedtls_x509_crt_parse returned -0x%x while parsing root cert\r\n", -ret);
return -1;
}
printf("ok! mbedtls_x509_crt_parse returned -0x%x while parsing root cert\r\n", -ret);
uint8_t ip_temp[4];
if (!is_ipaddr((uint8_t *)domain_name, (uint8_t *)ip_temp))
{
if ((ret = mbedtls_ssl_set_hostname(tlsContext->ssl, domain_name)) != 0)
{
printf(" failed mbedtls_ssl_set_hostname returned %d\r\n", ret);
return -1;
}
}
else
{
if ((ret = mbedtls_ssl_set_hostname(tlsContext->ssl, NULL)) != 0)
{
printf(" failed mbedtls_ssl_set_hostname returned %d\r\n", ret);
return -1;
}
}
printf("ok! mbedtls_ssl_set_hostname returned %d\r\n", ret);
tlsContext->root_ca_option = MBEDTLS_SSL_VERIFY_REQUIRED;
}
else
{
tlsContext->root_ca_option = MBEDTLS_SSL_VERIFY_NONE;
}
if (client_cert != NULL && pkey != NULL)
{
uint32_t client_cert_len = strlen(client_cert);
uint32_t pkey_len = strlen(pkey);
ret = mbedtls_x509_crt_parse((tlsContext->clicert), (const unsigned char *)client_cert, client_cert_len + 1);
if (ret != 0)
{
printf(" failed\r\n ! mbedtls_x509_crt_parse returned -0x%x while parsing device cert\r\n", -ret);
return -1;
}
printf("ok! mbedtls_x509_crt_parse returned -0x%x while parsing device cert\r\n", -ret);
ret = mbedtls_pk_parse_key(tlsContext->pkey, (const unsigned char *)pkey, pkey_len + 1, NULL, 0, mbedtls_ctr_drbg_random, tlsContext->ctr_drbg);
if (ret != 0)
{
printf(" failed\r\n ! mbedtls_pk_parse_key returned -0x%x while parsing private key\r\n", -ret);
return -1;
}
printf("ok! mbedtls_pk_parse_key returned -0x%x while parsing private key\r\n", -ret);
}
if ((ret = mbedtls_ssl_config_defaults(tlsContext->conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT))
!= 0)
{
printf(" failed mbedtls_ssl_config_defaults returned %d\r\n", ret);
return -1;
}
printf("socket_fd = %d\r\n", tlsContext->socket_fd);
mbedtls_ssl_conf_authmode(tlsContext->conf, tlsContext->root_ca_option);
mbedtls_ssl_conf_ca_chain(tlsContext->conf, tlsContext->cacert, NULL);
mbedtls_ssl_conf_rng(tlsContext->conf, SSLRandomCB, tlsContext->ctr_drbg);
if (client_cert != NULL && pkey != NULL)
{
if ((ret = mbedtls_ssl_conf_own_cert(tlsContext->conf, tlsContext->clicert, tlsContext->pkey)) != 0)
{
printf("failed! mbedtls_ssl_conf_own_cert returned %d\r\n", ret);
return -1;
}
printf("ok! mbedtls_ssl_conf_own_cert returned %d\r\n", ret);
}
mbedtls_ssl_conf_endpoint(tlsContext->conf, MBEDTLS_SSL_IS_CLIENT);
if (recv_timeout == 0)
recv_timeout = 2000;
mbedtls_ssl_conf_read_timeout(tlsContext->conf, recv_timeout);
if ((ret = mbedtls_ssl_setup(tlsContext->ssl, tlsContext->conf)) != 0)
{
printf(" failed mbedtls_ssl_setup returned -0x%x\r\n", -ret);
return -1;
}
mbedtls_ssl_set_bio(tlsContext->ssl, (void *)tlsContext->socket_fd, SSLSendCB, SSLRecvCB, SSLRecvTimeOutCB);
printf("return 1\r\n");
return 1;
}
在该测试过程中(设置了2秒超时和目标服务器IP,但未配置证书验证参数)。
/* tls init */
wiz_tls_init(&tlsContext, 2000, (char *)ssl_target_ip, NULL, NULL, NULL);
TLS握手过程:启动与服务器的TLS连接,调用W55MH32基于硬件的TRNG来生成随机数,并使用SHA引擎执行验证 。
TRNG_Out函数通过开启或关闭TRNG(真随机数生成器)模块,为TLS握手过程提供必要的真随机数来源,保障TLS协议中密钥生成等安全操作所需的随机熵源。
/**
* @brief TRNG_Stop
* @param
* @retval None
*/
void TRNG_Out(FunctionalState NewState)
{
if (NewState != DISABLE)
{
RCC->RCC_SYSCFG_CONFIG = 0x01;
SYSCFG->SYSCFG_LOCK = 0xCDED3526;
SYSCFG->SSC_CLK_EN |= TRNG_RNG_ENABLE;
}
else
{
RCC->RCC_SYSCFG_CONFIG = 0x00;
SYSCFG->SSC_CLK_EN &= ~TRNG_RNG_ENABLE;
}
}
/************************** (C) COPYRIGHT 2024 WIZnet *****END OF FILE****/
wiz_tls_connect是 TLS 客户端核心函数,作用是执行 TLS 握手:通过循环调用mbedtls_ssl_handshake处理握手流程, 启用证书验证时会校验服务器证书合法性(该测试为无校验证书模式),握手成功后输出协商的加密套件,最终完成 TLS 安全连接建立或返回错误。
int wiz_tls_connect(wiz_tls_context *tlsContext, char *addr, unsigned int port)
{
int ret;
uint32_t start_ms = millis(), flags;
printf(" Performing the SSL/TLS handshake...\r\n");
while ((ret = mbedtls_ssl_handshake(tlsContext->ssl)) != 0)
{
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
{
//mbedtls_strerror(ret, (char *) tempBuf, DEBUG_BUFFER_SIZE );
//printf( " failed\n\r ! mbedtls_ssl_handshake returned %d: %s\n\r", ret, tempBuf );
printf(" failed\n\r ! mbedtls_ssl_handshake returned -0x%x\n\r", -ret);
return (-1);
}
delay_ms(10);
}
if (tlsContext->root_ca_option == MBEDTLS_SSL_VERIFY_REQUIRED)
{
printf(" . Verifying peer X.509 certificate...\r\n");
/* In real life, we probably want to bail out when ret != 0 */
if ((flags = mbedtls_ssl_get_verify_result(tlsContext->ssl)) != 0)
{
char vrfy_buf[512];
printf("failed\r\n");
mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), " ! ", flags);
printf("%s\r\n", vrfy_buf);
return -1;
}
else
{
printf("ok\r\n");
}
}
printf(" ok\n\r [ Ciphersuite is %s ]\n\r",
mbedtls_ssl_get_ciphersuite(tlsContext->ssl));
return (0);
}
mbedtls_ssl_handshake函数是mbedTLS库中执行SSL/TLS握手协议的核心实现,通过循环调用握手步骤函数完成加密参数协商、密钥交换和身份验证等过程, 直到握手完成或出现错误,最终建立起客户端与服务器之间的安全加密通信通道。
/*
* Perform the SSL handshake
*/
int mbedtls_ssl_handshake(mbedtls_ssl_context *ssl)
{
int ret = 0;
/* Sanity checks */
if (ssl == NULL || ssl->conf == NULL) {
return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
}
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
(ssl->f_set_timer == NULL || ssl->f_get_timer == NULL)) {
MBEDTLS_SSL_DEBUG_MSG(1, ("You must use "
"mbedtls_ssl_set_timer_cb() for DTLS"));
return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
}
#endif /* MBEDTLS_SSL_PROTO_DTLS */
MBEDTLS_SSL_DEBUG_MSG(2, ("=> handshake"));
/* Main handshake loop */
while (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) {
ret = mbedtls_ssl_handshake_step(ssl);
if (ret != 0) {
break;
}
}
MBEDTLS_SSL_DEBUG_MSG(2, ("<= handshake"));
return ret;
}
#if defined(MBEDTLS_SSL_RENEGOTIATION)
#if defined(MBEDTLS_SSL_SRV_C)
4.安全通信:握手完成后,应用程序可以发送和接收数据 。利用W55MH32的AES和SHA硬件来处理所有加密、解密和完整性检查,从而使主CPU能够专注于应用逻辑 。
TLS 握手成功后,所有数据通过加密通道收发,核心是调用wiz_tls_write(加密发送)和wiz_tls_read(加密接收)。
unsigned int wiz_tls_read(wiz_tls_context *tlsContext, unsigned char *readbuf, unsigned int len)
{
return mbedtls_ssl_read(tlsContext->ssl, readbuf, len);
}
unsigned int wiz_tls_write(wiz_tls_context *tlsContext, unsigned char *writebuf, unsigned int len)
{
return mbedtls_ssl_write(tlsContext->ssl, writebuf, len);
}
通过已建立的TLS安全连接向百度服务器发送HTTP GET请求获取网页内容,并在无限循环中持续接收和打印服务器返回的响应数据, 实现了一个简单的HTTPS客户端功能来获取并显示百度首页的HTML内容。
if (1)
{
printf("W55MH32 get baidu.com html content......\r\n");
mbedtls_ssl_conf_read_timeout(tlsContext.conf, 200);
sprintf(ethernet_buf, "GET / HTTP/1.1\r\nHost: www.baidu.com\r\nUser-Agent: W55MH32/1.0\r\nAccept: text/html\r\nConnection: close\r\n\r\n");
wiz_tls_write(&tlsContext, (unsigned char *)ethernet_buf, strlen(ethernet_buf));
while (1)
{
recv_len = wiz_tls_read(&tlsContext, ethernet_buf, ETHERNET_BUF_MAX_SIZE - 1);
if (recv_len > 0)
{
printf("recv_len:%d\r\n", recv_len);
ethernet_buf[recv_len] = '\0';
printf("Received: %s", ethernet_buf);
}
}
}
运行结果
我们通过例程测试使用 HTTPS 获取百度网页信息,通过串口打印显示W55MH32 PHY链路正常、DHCP获网成功,加载加密库后完成TLS握手 (套件TLS-RSA-WITH-AES-128-GCM-SHA256),发送请求并接收百度响应。


W55MH32 启动 MbedTLS 传输测试并尝试获取百度 HTML 内容,成功接收 490 字节数据, 内容为百度返回的响应信息。

TLS 握手过程报文分析
通过Wireshark进行抓包展示了从 TCP 三次握手建立连接,接着开展 TLS 握手(涵盖客户端 hello、服务器 hello、密钥交换等操作)以构建安全通信通道并传输应用数据。

我们知道,HTTPS协议是基于TCP/IP协议的,所以在TLS握手前客户端与百度服务器分别进行了TCP三次握手。

接着可以看到TLS握手建立安全通道的过程,在Wireshark的显示过滤器中输入表达式ip.addr == 192.168.1.13 and tls过滤出TLS协议数据包, W55MH32为客户端(192.168.1.13)访问百度服务器(182.61.244.181)。

第一次握手报文(164):W55MH32->百度 发送TLS消息「Client Hello」;
第二次握手报文(166、176):
- 百度->W55MH32 发送TLS消息「Server Hello」;
- 百度->W55MH32 发送TLS消息「Certificate」,「Server Hello Done」;
第三次握手报文(192、193、194):W55MH32->百度 发送TLS消息「Client Key Exchange」,「Change Cipher Spec」,「Encrypted Handshake Message」;
第四次握手报文(200):百度->W55MH32 发送TLS消息「Change Cipher Spec」,「Encrypted Handshake Message」。 其握手过程如图所示:

TLS第一次握手:client -> server (Client Hello) 客户端向服务端发送:
- 客户端支持的协议;
- 已经使用的TLS版本;
- 随机数Random;
- 支持的密码套件。
接下来我们看几个TLS协议中的重要字段(Version、Random、SessionID和Cipher Suites)。

从上图中可以看到当前通信安全层协议版本(Version)为TLS 1.2。其实TLS共总有4个版本,最新为TLS 1.3,具体如下:
- TLS 1.3:发布于2018年,TLS 1.3是TLS协议的最新版本,大幅简化了握手过程,提高了性能和安全性。它删除了一些不安全的加密算法,并引入了0-RTT(零往返时间)握手,以减少延迟。
- TLS 1.2:TLS 1.2发布于2008年,它引入了更强的加密算法和更灵活的密码套件选择,修复了许多安全漏洞,是目前广泛使用的版本。(当前样例中百度网站使用的就是TLS 1.2协议)
- TLS 1.1:TLS 1.1发布于2006年,引入了一些改进,包括对CBC(Cipher Block Chaining)模式的安全性改进,但仍然存在一些已知的安全问题。
- TLS 1.0:TLS 1.0于1999年发布,是SSL 3.0的升级版本,修复了一些安全漏洞,但仍然存在一些安全问题。
Random用于密钥的制作,这里我们将第一次握手中客户端发送给服务端的随机数记为Client Random。
SessionID用来表示客户端是否想复用先前存在的session。如果不复用,则此字段为0;如果想要复用,则此字段为想要复用的sessionID,是否复用由服务端确定。
Cipher Suites密码套件列表会全部发给服务端,服务端从中挑选一个最安全的密码套件返回客户端。
密码套件的格式通常由一系列的加密算法和相关参数组成。一般来说,它的格式包含以下几个部分:
- 密钥交换算法:用于协商会话密钥,例如 Diffie-Hellman(DHE)、ECDHE等。
- 身份验证算法:也称签名算法用于验证对端身份,例如RSA。
- 加密算法:用于实际数据传输的加解密,例如 AES(Advanced Encryption Standard)、RC4 等。
- 数据摘要算法:用于计算哈希,主要是用于验证数据的完整性,防止信息被篡改。
请注意:
格式:「密钥交换算法 + 签名算法(认证算法) + 对称加密算法 + 摘要算法」。其中密钥交换算法和签名算法可以相同,此时就会合二为一。
不同的密码套件组合提供了不同的安全性和性能特点,服务器和客户端在进行 TLS 握手时会协商选择双方都支持的 Cipher Suite 来建立安全连接。
客户端发送完「Client Hello」,服务端向客户端发送的ACK确认消息,代表上面的「Client Hello」已经收到。这里也可以看出服务端是通过普通的TCP 的ACK消息去应答「Client Hello」。

TLS第二次握手:server -> client (1.Server Hello; 2.Certificate,Server key Exchange,Server Hello Done) 这一次握手时,server向client连续发送了两个报文:
- Server Hello:服务端为了证明自己的身份,会发送「Server Certificate」给客户端,这个消息里含有数字证书;
- Certificate,Server Hello Done:目的是告诉客户端,我已经把该给你的东西都给你了,本次打招呼完毕。
- 服务端支持的协议;
- 使用的TLS版本;
- 随机数Random;
- 服务端选用的密码套件。

上图中可以看到,服务端也生成了一个随机数,并发送给了客户端,服务端生成的随机数记为Server Random。
客户端和服务端就已确认了 TLS 版本和使用的密码套件TLS_RSA_WITH_AES_128_GCM_SHA256。
服务端发送报文:Certificate,Server Hello Done。

TLS第三次握手client -> server (Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message)
客户端向服务器传递加密预主密钥,更改密码规范,和加密握手的消息。


TLS 第四次握手server -> client (Change Cipher Spec,Encrypted Handshake Message)
服务器也是同样的操作,发送「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双方都验证加密和解密没问题,那么TLS握手正式完成。
TLS握手完成后,客户端与服务端的所有通信内容均被加密,如果没有会话密钥则无法解密通信内容。


服务器遵循 TLS 协议规范发出关闭警报(close_notify),随即发送 TCP RST 报文以快速释放连接资源。
结论
在像嵌入式微控制器这样的受限设备上实现TLS 1.2是一项具有挑战性但又必不可少的安全连接任务 。通过利用TRNG、SHA256和AES256等硬件加速功能,开发人员可以创建健壮且高性能的安全通信通道 。W55MH32为这些功能提供了专用硬件, 包括其硬件加密算法单元和TRNG,使得主CPU能够专注于应用逻辑,这对于高性能和安全的嵌入式系统至关重要 。