coreHTTP Demo (Mutual Authentication)
Note: We recommend to always use mutual authentication in building any Internet of Things (IoT) application.
Single Threaded Vs Multi Threaded
There are two coreHTTP usage models, single threaded and multithreaded (multitasking). Although the demo on this page runs the HTTP library in a thread, it is actually demonstrating how to use coreHTTP in a single threaded environment (only one task uses the HTTP API in the demo). Whereas single threaded applications must repeatedly call the HTTP library, multithreaded applications instead can execute sending HTTP requests in the background within an agent (or daemon) task.
Demo Introduction
The coreHTTP (Mutual Authentication) demo uses a network transport interface that uses mbedTLS to establish a mutually authenticated connection between an IoT device client running coreHTTP and a remote HTTP server. This demo can connect to any HTTP server capable of mutually authenticated connections. Once connected the demo creates an HTTP request, then sends the request and receives the response. The instructions below describe how to connect to the Amazon Web Services (AWS) IoT HTTP server.
This example project is one of two that introduce the concepts described on the "TLS Introduction" page one at a time. The first example demonstrates unencrypted HTTP communication. The second example (on this page) builds on the first to introduce strong mutual authentication (where the HTTP server also authenticates the client connecting to it).
The coreHTTP (Mutual Authentication) demo project uses the FreeRTOS Windows port, enabling it to be built and evaluated with the free Community version of Visual Studios on Windows, so without the need for any particular MCU hardware.
Source Code Organization
The demo project is called http_mutual_auth_demo.sln and is located in the FreeRTOS-Plus/Demo/coreHTTP_Windows_Simulator/HTTP_Mutual_Auth
directory of the main FreeRTOS download (links to Github are also on the download page).
The source code is organized as per the basic HTTP demo (without TLS).
Configuring the HTTP Server Connection
The Mutual Authentication HTTP demo requires client authentication and server authentication. As most public HTTP servers do not authenticate the client, this demo will showcase a connection to AWS (Amazon Web Services) IoT. Additional steps are required to acquire and set up credentials using existing tools provided by AWS. For enhanced security, AWS IoT does not support plaintext and server side only authentication. See Security in AWS, for more details.
Follow the steps below for configuring your connection to AWS.
- Set up an Amazon Web Services (AWS) account:
- Accounts and permissions are set using AWS Identity and Access Management (IAM). IAM allows you to manage the permissions for each user. By default, no users have permissions until granted by the root owner.
- To add an IAM user to your AWS account, see the IAM User Guide.
- Set permissions for your AWS account to access FreeRTOS and AWS IoT by adding the policies below:
- Create AWS IoT client certificates using the AWS IoT Core console.
-
- Add a device to AWS IoT Console
- Follow these steps to create a private key and certificate in AWS IoT. Immediately download the certificate and private key created. You will also need your AWS IoT endpoint found in the following steps:
- Browse to the AWS IoT console.
- In the navigation pane, choose Settings.
Your AWS IoT endpoint is displayed as the Custom Endpoint. It should look like <account-number>
-ats.iot.<us-east-1>
.amazonaws.com
.
- Once you have completed the setup on the service side, you need to configure credentials for AWS IoT credentials on the client side. Paste the endpoint and credentials into
FreeRTOS-Plus/Demo/coreHTTP_Windows_Simulator/HTTP_Mutual_Auth/demo_config.h
:
-
- Copy the AWS IoT Custom Endpoint into
#define democonfigAWS_IOT_ENDPOINT "<here>"
.
- Copy the root CA certificate that you downloaded from the AWS IoT console into
#define democonfigROOT_CA_PEM "<here>"
.
- Copy the client certificate that you downloaded from the AWS IoT console into
#define democonfigCLIENT_CERTIFICATE_PEM "<here>"
.
- Copy the client private key that you downloaded from the AWS IoT console into
#define democonfigCLIENT_PRIVATE_KEY_PEM "<here>"
.
Build the Demo Project
You build this demo project in the same way as the basic HTTP demo (without TLS). The demo project uses the free community edition of Visual Studio. To build this demo
- Open the
FreeRTOS-Plus/Demo/coreHTTP_Windows_Simulator/HTTP_Mutual_Auth/http_mutual_auth_demo.sln
Visual Studio solution file from within the Visual Studio IDE
- Select Build Solution from the IDE’s Build menu
NOTE: If you are using Microsoft Visual Studio 2017 or earlier, then you must select a Platform Toolset compatible with your version: Project -> RTOSDemos Properties -> Platform Toolset
Functionality
The demo provides the same functionality as the basic HTTP demo with the addition of a secure connection to your AWS IoT endpoint. For details on the additional functionality (creating an HTTP request, sending the request, and receiving the response) please view the basic HTTP demo (without TLS).
The demo creates a single application task that demonstrate how to connect to the AWS IoT HTTP server, create an HTTP request, send the HTTP request and recieve the HTTP response, then finally, disconnect from the server. The structure of the demo is shown below.
static void prvHTTPDemoTask( void * pvParameters )
{
TransportInterface_t xTransportInterface;
NetworkContext_t xNetworkContext = { 0 };
TlsTransportParams_t xTlsTransportParams = { 0 };
BaseType_t xIsConnectionEstablished = pdFALSE;
BaseType_t xDemoStatus = pdPASS;
( void ) pvParameters;
xNetworkContext.pParams = &xTlsTransportParams;
xDemoStatus = connectToServerWithBackoffRetries( prvConnectToServer,
&xNetworkContext );
if( xDemoStatus == pdPASS )
{
xIsConnectionEstablished = pdTRUE;
xTransportInterface.pNetworkContext = &xNetworkContext;
xTransportInterface.send = TLS_FreeRTOS_send;
xTransportInterface.recv = TLS_FreeRTOS_recv;
}
else
{
LogError( ( "Failed to connect to HTTP server %.*s.",
( int32_t ) AWS_IOT_ENDPOINT_LENGTH,
democonfigAWS_IOT_ENDPOINT ) );
}
if( xDemoStatus == pdPASS )
{
xDemoStatus = prvSendHttpRequest( &xTransportInterface,
HTTP_METHOD_POST,
( sizeof( HTTP_METHOD_POST ) - 1 ),
democonfigPOST_PATH,
( sizeof( democonfigPOST_PATH ) - 1 ) );
}
if( xIsConnectionEstablished == pdTRUE )
{
TLS_FreeRTOS_Disconnect( &xNetworkContext );
}
if( xDemoStatus == pdPASS )
{
LogInfo( ( "prvHTTPDemoTask() completed successfully. "
"Total free heap is %u.\r\n",
xPortGetFreeHeapSize() ) );
LogInfo( ( "Demo completed successfully.\r\n" ) );
}
}
Connect to the HTTP server (with mutual authentication)
The function connectToServerWithBackoffRetries()
attempts to make a mutually authenticated TLS connection to the HTTP server. If the connection fails, it retries after a timeout. The timeout value will exponentially increase until the maximum number of attempts are reached or the maximum timeout value is reached. The function BackoffAlgorithm_GetNextBackoff()
provides exponentially increasing timeout value and returns BackoffAlgorithmRetriesExhausted
when the maximum number of attempts have been reached. connectToServerWithBackoffRetries()
returns a failure status if the TLS connection cannot be established to the server after the configured number of attempts.
BaseType_t connectToServerWithBackoffRetries( TransportConnect_t connectFunction,
NetworkContext_t * pxNetworkContext )
{
BaseType_t xReturn = pdFAIL;
BackoffAlgorithmStatus_t xBackoffAlgStatus = BackoffAlgorithmSuccess;
BackoffAlgorithmContext_t xReconnectParams;
uint16_t usNextBackoff = 0U;
assert( connectFunction != NULL );
BackoffAlgorithm_InitializeParams( &xReconnectParams,
RETRY_BACKOFF_BASE_MS,
RETRY_MAX_BACKOFF_DELAY_MS,
RETRY_MAX_ATTEMPTS );
do
{
xReturn = connectFunction( pxNetworkContext );
if( xReturn != pdPASS )
{
LogWarn( ( "Connection to the HTTP server failed. "
"Retrying connection with backoff and jitter." ) );
LogInfo( ( "Retry attempt %lu out of maximum retry attempts %lu.",
( xReconnectParams.attemptsDone + 1 ),
RETRY_MAX_ATTEMPTS ) );
xBackoffAlgStatus = BackoffAlgorithm_GetNextBackoff( &xReconnectParams, uxRand(), &usNextBackoff );
}
} while( ( xReturn == pdFAIL ) && ( xBackoffAlgStatus == BackoffAlgorithmSuccess ) );
if( xReturn == pdFAIL )
{
LogError( ( "Connection to the server failed, all attempts exhausted." ) );
}
return xReturn;
}
The function prvConnectToServer()
demonstrates how to establish an HTTP connection to a server. It uses the TLS transport interface which is implemented in the file FreeRTOS-Plus/Source/Application-Protocols/network_transport/freertos_plus_tcp/using_mbedtls/using_mbedtls.c
. The definition of prvConnectToServer()
is shown below.
static BaseType_t prvConnectToServer( NetworkContext_t * pxNetworkContext )
{
BaseType_t xStatus = pdPASS;
TlsTransportStatus_t xNetworkStatus;
NetworkCredentials_t xNetworkCredentials = { 0 };
configASSERT( pxNetworkContext != NULL );
if( democonfigAWS_HTTP_PORT == 443 )
{
static const char * pcAlpnProtocols[] = { IOT_CORE_ALPN_PROTOCOL_NAME, NULL };
xNetworkCredentials.pAlpnProtos = pcAlpnProtocols;
}
xNetworkCredentials.disableSni = democonfigDISABLE_SNI;
xNetworkCredentials.pRootCa = ( const unsigned char * ) democonfigROOT_CA_PEM;
xNetworkCredentials.rootCaSize = sizeof( democonfigROOT_CA_PEM );
xNetworkCredentials.pClientCert = ( const unsigned char * ) democonfigCLIENT_CERTIFICATE_PEM;
xNetworkCredentials.clientCertSize = sizeof( democonfigCLIENT_CERTIFICATE_PEM );
xNetworkCredentials.pPrivateKey = ( const unsigned char * ) democonfigCLIENT_PRIVATE_KEY_PEM;
xNetworkCredentials.privateKeySize = sizeof( democonfigCLIENT_PRIVATE_KEY_PEM );
LogInfo( ( "Establishing a TLS session to %.*s:%d.",
( int32_t ) AWS_IOT_ENDPOINT_LENGTH,
democonfigAWS_IOT_ENDPOINT,
democonfigAWS_HTTP_PORT ) );
xNetworkStatus = TLS_FreeRTOS_Connect( pxNetworkContext,
democonfigAWS_IOT_ENDPOINT,
democonfigAWS_HTTP_PORT,
&xNetworkCredentials,
democonfigTRANSPORT_SEND_RECV_TIMEOUT_MS,
democonfigTRANSPORT_SEND_RECV_TIMEOUT_MS );
if( xNetworkStatus != TLS_TRANSPORT_SUCCESS )
{
xStatus = pdFAIL;
}
return xStatus;
}
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.