网上有很多移植MbedTLS,也有很多移植LWIP的,但是却没有移植如何访问HTTPS?
环境准备:
- 随机数 (硬件支持更好,会有更高的熵)
- RTC (针对SSL需要一个准确的时间)
- 公钥 (单片机储存有限,不可能储存所有公钥)
- 内存管理 (使用calloc/malloc不是不好,容易碎片)
时间的处理.
extern RTC_HandleTypeDef hrtc;
void sntp_set_system_time(time_t sntp_time)
{
struct tm *tm;
RTC_DateTypeDef RTC_DateStructure;
RTC_TimeTypeDef RTC_TimeStructure;
tm = gmtime(&sntp_time);
RTC_DateStructure.Year = tm->tm_year + 1900 - 2000;
RTC_DateStructure.Month = tm->tm_mon + 1;
RTC_DateStructure.Date = tm->tm_mday;
RTC_DateStructure.WeekDay = tm->tm_wday;
RTC_TimeStructure.Hours = tm->tm_hour + 8; /* UTC + 8 */
RTC_TimeStructure.Minutes = tm->tm_min;
RTC_TimeStructure.Seconds = tm->tm_sec;
RTC_TimeStructure.TimeFormat = RTC_HOURFORMAT12_AM;
RTC_TimeStructure.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
RTC_TimeStructure.StoreOperation = RTC_STOREOPERATION_RESET;
HAL_RTC_SetDate(&hrtc, &RTC_DateStructure, RTC_FORMAT_BIN);
HAL_RTC_SetTime(&hrtc, &RTC_TimeStructure, RTC_FORMAT_BIN);
}
time_t rtc_get_system_time(time_t * timer){
struct tm tm;
RTC_DateTypeDef RTC_DateStructure;
RTC_TimeTypeDef RTC_TimeStructure;
if(timer != NULL){
return time(timer);
}
HAL_RTC_GetTime(&hrtc, &RTC_TimeStructure, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &RTC_DateStructure, RTC_FORMAT_BIN);
tm.tm_hour = RTC_TimeStructure.Hours;
tm.tm_min = RTC_TimeStructure.Minutes;
tm.tm_sec = RTC_TimeStructure.Seconds;
tm.tm_year = RTC_DateStructure.Year + 100;
tm.tm_mon = RTC_DateStructure.Month;
tm.tm_mday = RTC_DateStructure.Date;
tm.tm_wday = RTC_DateStructure.WeekDay;
tm.tm_yday = 0; /* Can't Easy Calc,waste time,so ignore it. */
return mktime(&tm);
}
公钥获取:https://letsencrypt.org/certificates/ (不同签发的人不同地方,我用的Let's CertBot)
把密钥改成数组.
const char r3_ssl[] =
"-----BEGIN CERTIFICATE-----\r\n" \
"MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\r\n" \
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\r\n" \
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\r\n" \
"WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\r\n" \
"RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\r\n" \
"AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\r\n" \
"R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\r\n" \
"sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\r\n" \
"NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\r\n" \
"Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\r\n" \
"/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\r\n" \
"AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\r\n" \
"Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\r\n" \
"FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\r\n" \
"AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\r\n" \
"Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\r\n" \
"gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\r\n" \
"PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\r\n" \
"ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\r\n" \
"CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\r\n" \
"lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\r\n" \
"avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\r\n" \
"yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\r\n" \
"yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\r\n" \
"hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\r\n" \
"HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\r\n" \
"MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\r\n" \
"nLRbwHOoq7hHwg==\r\n" \
"-----END CERTIFICATE-----\r\n";
从LWIP封装一层到MbedTLS.
#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
#include <string.h>
#include <stdint.h>
#if defined(MBEDTLS_NET_C)
#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
#else
#include <stdlib.h>
#endif
#include "mbedtls/net_sockets.h"
#include "lwip/dhcp.h"
#include "lwip/tcpip.h"
#include "lwip/netdb.h"
#include "lwip/sockets.h"
#include "netif/ethernet.h"
#include "ethernetif.h"
#include "stm32h7xx_hal.h"
static int net_would_block( const mbedtls_net_context *ctx );
/*
* Initialize LwIP stack and get a dynamic IP address.
*/
void mbedtls_net_init( mbedtls_net_context *ctx )
{
/* By LWIP Main Stack */
}
/*
* Initiate a TCP connection with host:port and the given protocol
*/
int mbedtls_net_connect( mbedtls_net_context *ctx, const char *host, const char *port, int proto )
{
int ret;
struct addrinfo hints;
struct addrinfo *list;
struct addrinfo *current;
/* Do name resolution with both IPv6 and IPv4 */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = proto == MBEDTLS_NET_PROTO_UDP ? SOCK_DGRAM : SOCK_STREAM;
hints.ai_protocol = proto == MBEDTLS_NET_PROTO_UDP ? IPPROTO_UDP : IPPROTO_TCP;
if(getaddrinfo(host, port, &hints, &list) != 0)
return MBEDTLS_ERR_NET_UNKNOWN_HOST;
/* Try the sockaddrs until a connection succeeds */
ret = MBEDTLS_ERR_NET_UNKNOWN_HOST;
for( current = list; current != NULL; current = current->ai_next)
{
ctx->fd = (int) socket(current->ai_family, current->ai_socktype, current->ai_protocol);
if(ctx->fd < 0)
{
ret = MBEDTLS_ERR_NET_SOCKET_FAILED;
continue;
}
if(connect(ctx->fd, current->ai_addr, (uint32_t)current->ai_addrlen) == 0)
{
ret = 0;
break;
}
close( ctx->fd );
ret = MBEDTLS_ERR_NET_CONNECT_FAILED;
}
freeaddrinfo(list);
return ret;
}
/*
* Create a listening socket on bind_ip:port
*/
int mbedtls_net_bind( mbedtls_net_context *ctx, const char *bind_ip, const char *port, int proto )
{
int ret = 0;
mbedtls_printf ("%s() NOT IMPLEMENTED!!\n", __FUNCTION__);
return ret;
}
/*
* Accept a connection from a remote client
*/
int mbedtls_net_accept( mbedtls_net_context *bind_ctx,
mbedtls_net_context *client_ctx,
void *client_ip, size_t buf_size, size_t *ip_len )
{
mbedtls_printf ("%s() NOT IMPLEMENTED!!\n", __FUNCTION__);
return 0;
}
/*
* Set the socket blocking or non-blocking
*/
int mbedtls_net_set_block( mbedtls_net_context *ctx )
{
mbedtls_printf ("%s() NOT IMPLEMENTED!!\n", __FUNCTION__);
return 0;
}
int mbedtls_net_set_nonblock( mbedtls_net_context *ctx )
{
mbedtls_printf ("%s() NOT IMPLEMENTED!!\n", __FUNCTION__);
return 0;
}
/*
* Portable usleep helper
*/
void mbedtls_net_usleep( unsigned long usec )
{
mbedtls_printf ("%s() NOT IMPLEMENTED!!\n", __FUNCTION__);
}
/*
* Read at most 'len' characters
*/
int mbedtls_net_recv( void *ctx, unsigned char *buf, size_t len )
{
int32_t ret;
fd_set readset;
int32_t fd = ((mbedtls_net_context *) ctx)->fd;
if( fd < 0 )
{
return MBEDTLS_ERR_NET_INVALID_CONTEXT;
}
do
{
FD_ZERO(&readset);
FD_SET(fd, &readset);
ret = select(fd + 1, &readset, NULL, NULL, NULL);
} while (ret == -1 && errno == EINTR);
if (ret > 0)
{
if (FD_ISSET(fd, &readset))
{
ret = (int32_t) read( fd, buf, len );
}
}
else
{
if(net_would_block(ctx) != 0)
{
return MBEDTLS_ERR_SSL_WANT_READ;
}
if(errno == EPIPE || errno == ECONNRESET)
{
return MBEDTLS_ERR_NET_CONN_RESET;
}
if(errno == EINTR)
{
return MBEDTLS_ERR_SSL_WANT_READ;
}
return MBEDTLS_ERR_NET_RECV_FAILED;
}
return ret;
}
/*
* Read at most 'len' characters, blocking for at most 'timeout' ms
*/
int mbedtls_net_recv_timeout( void *ctx, unsigned char *buf, size_t len,
uint32_t timeout )
{
int ret = 0;
mbedtls_printf ("%s() NOT IMPLEMENTED!!\n", __FUNCTION__);
return ret;
}
static int net_would_block( const mbedtls_net_context *ctx )
{
/*
* Never return 'WOULD BLOCK' on a non-blocking socket
*/
int val = 0;
UNUSED(val);
if( ( fcntl( ctx->fd, F_GETFL, val) & O_NONBLOCK ) != O_NONBLOCK )
return( 0 );
switch( errno )
{
#if defined EAGAIN
case EAGAIN:
#endif
#if defined EWOULDBLOCK && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
return( 1 );
}
return( 0 );
}
/*
* Write at most 'len' characters
*/
int mbedtls_net_send( void *ctx, const unsigned char *buf, size_t len )
{
int32_t ret;
int fd = ((mbedtls_net_context *) ctx)->fd;
if( fd < 0 )
{
return MBEDTLS_ERR_NET_INVALID_CONTEXT;
}
ret = (int32_t) write(fd, buf, len);
if( ret < 0 )
{
if(net_would_block(ctx) != 0)
{
return MBEDTLS_ERR_SSL_WANT_WRITE;
}
if(errno == EPIPE || errno == ECONNRESET)
{
return MBEDTLS_ERR_NET_CONN_RESET;
}
if(errno == EINTR)
{
return MBEDTLS_ERR_SSL_WANT_WRITE;
}
return MBEDTLS_ERR_NET_SEND_FAILED;
}
return ret;
}
/*
* Gracefully close the connection
*/
void mbedtls_net_free( mbedtls_net_context *ctx )
{
if( ctx->fd == -1 )
return;
shutdown( ctx->fd, 2 );
close( ctx->fd );
ctx->fd = -1;
}
#endif /* MBEDTLS_NET_C */
那么大功告成了,工程参考:
https://github.com/nickfox-taterli/NUCLEO-H743-NET
当然,请自建服务器测试.