Connect to web services

The Azure Sphere SDK includes the libcurl library, which high-level applications can use to connect and authenticate with HTTP and HTTPS web services. Both server and client authentication are supported, so that applications can verify that they are communicating with the expected server and can prove to the server that their device and Azure Sphere tenant are legitimate. Mutual authentication combines the two.

The Azure Sphere samples repo on GitHub includes the following curl samples:

Although the synchronous approach to server authentication in HTTPS_Curl_Easy is quite simple, Azure Sphere applications should generally use the more complex asynchronous technique shown in the HTTPS_Curl_Multi sample, along with an epoll-based, single-threaded event-driven pattern.

The libcurl website provides thorough documentation of the libcurl C API and many examples.

Requirements for applications that use curl

Applications that use the curl library must include the appropriate header files, and provide tenant and internet host information in the application manifest.

Header files

To use curl, include these header files in your application:

#include <applibs/storage.h>  // required only if you supply a certificate in the image package
#include <tlsutils/deviceauth_curl.h> // required only for mutual authentication
#include <curl/curl.h>

The storage.h header file is required only if you supply one or more certificates in the application image package. The deviceauth_curl.h header is required for performing mutual authentication.

Application manifest

The AllowedConnections field of the application manifest must specify the hosts to which the application connects. It must also contain the name of each domain that the connection may encounter if redirected. For example, both microsoft.com and www.microsoft.com are required for an application that connects to the Microsoft home page.

If the application uses mutual authentication, the DeviceAuthentication field of the manifest must include the Azure Sphere tenant ID. Device authentication certificates are issued only if the device's tenant ID matches the tenant ID in the application manifest. This restriction provides defense in depth: an application running on a device in a different tenant (say, that of a different customer or a rogue entity) cannot authenticate to the server.

During development, you can find the ID of the current Azure Sphere tenant by using the azsphere tenant show-selected command.

In the following example, the AllowedConnections field specifies that the application connects only to www.example.com, and the DeviceAuthentication field specifies the Azure Sphere tenant ID and thus enables the application to use the device certificate for mutual authentication.

  "Capabilities": {
    "AllowedConnections": [ "www.example.com" ],
    "Gpio": [],
    "Uart": [],
    "WifiConfig": false,
    "DeviceAuthentication": "00000000-0000-0000-0000-000000000000"
  }

Supported functionality

Libcurl for Azure Sphere supports only the HTTP and HTTPS protocols. In addition, the Azure Sphere OS does not support some functionality, such as writable files (cookies) or UNIX sockets. Features that will not be supported in future libcurl releases, such as the mprintf() family, are not available.

Libcurl for Azure Sphere supports TLS 1.2 and has deprecated TLS 1.0 and TLS 1.1 in alignment with the broader Microsoft TLS security strategy.

The following are the supported cipher suites:

  • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
  • TLS_DHE_RSA_WITH_AES_128_CBC_SHA256

Attempts to use an unsupported version of TLS return the error CyaSSL does not support <version>.

Server authentication

Azure Sphere supports server authentication through libcurl. The server’s certificate must be signed by a Certificate Authority (CA) that the device trusts. For libcurl to authenticate a server, the application must provide the path to the CA file.

Add CA certificates to the image package

To use one or more CAs, you must add the certificates to your image package. Each certificate must be base-64 encoded. The simplest approach is to create a single file that contains all the additional certificates. The file must have the .pem filename extension. To add certificates:

  1. Create a certs folder in the project folder for your application. The project folder contains the CMakeLists file for your application.

  2. In the certs folder, create a text file with the .pem extension, copy each certificate into it, and save the file.

  3. In the CMakeLists.txt file, add the path to the .pem file in a SET command after the compile and build steps but before the MakeImage post-build command. For example:

    SET(ADDITIONAL_APPROOT_INCLUDES "certs/DigiCertGlobalRootCA.pem") INCLUDE("${AZURE_SPHERE_MAKE_IMAGE_FILE}")

The certificate file should now appear in the certs folder in the image package.

Set certificate locations

In your application, use the CURLOPT_CAPATH and CURLOPT_CAINFO options to set the locations of the certificates. Call Storage_GetAbsolutePathInImagePackage to retrieve the absolute path to the certificates in the image package and then call curl_easy_setopt.

CURLOPT_CAPATH sets a default folder for the certificates. For example, the following code tells curl to look for certificates in the certs folder in the image:

char *path = Storage_GetAbsolutePathInImagePackage("certs");
curl_easy_setopt(curl_handle, CURLOPT_CAPATH, path);

CURLOPT_CAINFO sets a path to a file that contains one or more certificates. Curl searches this file in addition to the default folder set in CURLOPT_CAPATH.For example:

char *path = Storage_GetAbsolutePathInImagePackage("CAs/mycertificates.pem");
curl_easy_setopt(curl_handle, CURLOPT_CAINFO, path);

This code tells curl to trust any CAs that are defined in the mycertificates.pem file, in addition to CAs that are defined in the directory set in CURLOPT_CAPATH.

Mutual authentication

Mutual authentication verifies that both the server and the client device are legitimate. It's a multi-step process:

  1. The application authenticates the server using a CA certificate, as described in Server authentication.
  2. The application presents an x509 client authentication certificate to the server so that the server can authenticate the device.
  3. The server uses the Azure Sphere tenant's certificate chain to verify that the device belongs to the tenant.

An application can set up the device-authentication side of mutual authentication in either of two ways:

  • Configure the Azure Sphere DeviceAuth_CurlSslFunc function as the SSL function that performs authentication.
  • Create a custom SSL function that calls the Azure Sphere DeviceAuth_SslCtxFunc function for authentication.

Before you use either function, you must update the CMakeLists.txt file for your application to add curl and tlsutils to TARGET_LINK_LIBRARIES:

TARGET_LINK_LIBRARIES(${PROJECT_NAME} applibs pthread gcc_s c curl tlsutils)

Use DeviceAuth_CurlSslFunc

The simplest way to perform device authentication is to configure DeviceAuth_CurlSslFunc as the callback function for curl SSL authentication:

// Set DeviceAuth_CurlSslFunc to perform authentication
CURLcode err = curl_easy_setopt(_curl, CURLOPT_SSL_CTX_FUNCTION, DeviceAuth_CurlSslFunc);
if (err) {
	// Set ssl function failed
	return err;
}

The DeviceAuth_CurlSslFunc function retrieves the certificate chain for the current Azure Sphere tenant and sets up the curl connection to perform mutual authentication. If authentication fails, the function returns CURLE_SSL_CERTPROBLEM.

Use DeviceAuth_SslCtxFunc

An application can also use a custom SSL callback function that calls the Azure Sphere DeviceAuth_SslCtxFunc function for authentication.

Your custom SSL function must call DeviceAuth_SslCtxFunc to perform the authentication, but may also do other tasks related to authentication. DeviceAuth_SslCtxFunc returns a value of the DeviceAuthSslResult enumeration, which provides detailed information about the failure. For example:

static CURLcode MyCallback(CURL *curl, void *sslctx, void *userCtx)
{
    int err = DeviceAuth_SslCtxFunc(sslctx);
    Log_Debug("ssl func callback error %d\n", err);
    if (err) {
        // detailed error handling code goes here
    }
    return CURLE_OK;
}
...

err = curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, MyCallback);
    if (err) {
        goto cleanupLabel;
    }

Use the tenant certificate chain on your server

To perform mutual authentication, the server must be able to verify that the device belongs to your Azure Sphere tenant and that the tenant itself is legitimate. To perform this authentication, the server requires the Azure Sphere tenant's certificate chain, which signs all your Azure Sphere devices:

To get the certificate chain for your tenant, download it to a .p7b file, as in the following example:

azsphere tenant download-ca-certificate-chain --output CA-cert-chain.p7b

You can then use the .p7b file on your server.

Additional tips for using curl

Here are some additional tips for using curl in an Azure Sphere application.

  • If you plan to store page content in RAM or flash, keep in mind that storage on the Azure Sphere device is limited.

  • To ensure that curl follows redirects, add the following to your code:

    curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
    
  • To add verbose information about curl operations that might be helpful during debugging:

    curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
    
  • Some servers return errors if a request does not contain a user agent. To set a user agent:

    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");