Se connecter aux services web

Le Kit de développement logiciel (SDK) Azure Sphere inclut la bibliothèque libcurl, que les applications de haut niveau peuvent utiliser pour se connecter et s’authentifier auprès des services web HTTP et HTTPS. L’authentification du serveur et du client est prise en charge, de sorte que les applications peuvent vérifier qu’elles communiquent avec le serveur attendu et peuvent prouver au serveur que leur appareil et le catalogue Azure Sphere sont légitimes. L’authentification mutuelle combine les deux.

Le référentiel d’exemples Azure Sphere sur GitHub comprend les exemples curl suivants :

  • HTTPS_Curl_Easy utilise une API synchrone (bloquante) pour l’authentification du serveur.
  • HTTPS_Curl_Multi exemple utilise une API asynchrone (non bloquante) pour l’authentification du serveur.

Bien que l’approche synchrone de l’authentification serveur dans HTTPS_Curl_Easy soit assez simple, les applications Azure Sphere doivent généralement utiliser la technique asynchrone plus complexe présentée dans l’exemple de HTTPS_Curl_Multi, ainsi qu’un modèle basé sur époll, basé sur un seul thread basé sur les événements.

Le site web libcurl fournit une documentation complète de l’API C libcurl et de nombreux exemples. Les différences entre la bibliothèque cURL et la bibliothèque runtime du SDK Azure Sphere sont les suivantes :

Nom de la constante
(définition)
limites de plage de cURL Limites de la plage Azure Sphere
CURLOPT_BUFFERSIZE
(taille de la mémoire tampon)
Valeur par défaut : 16 Ko Valeur par défaut : 1536 Ko
CURLOPT_UPLOAD_BUFFERSIZE
(taille de la mémoire tampon de chargement)
Valeur par défaut : 64 Ko
Maximum : 2 Mo
Minimum : 16 Ko
Valeur par défaut : 1536 Ko
Maximum : 64 Ko
Minimum : 1536 Ko
CURLOPT_HEADERFUNCTION
(en-tête HTTP complet passé à cette fonction)
Maximum : 100 Ko Maximum : 16 Ko
CURLOPT_DNS_CACHE_TIMEOUT Valeur par défaut : mettre en cache les résultats pendant 60 secondes
Maximum : résultats du cache pour toujours
Minimum : 0 (ne pas mettre en cache les résultats)
Toutes les valeurs sont remplacées par 0 et les résultats ne sont pas mis en cache.

Configuration requise pour les applications qui utilisent curl

Les applications qui utilisent la bibliothèque curl doivent inclure les fichiers d’en-tête appropriés et fournir des informations sur l’UUID et l’hôte Internet du locataire Azure Sphere (hérité) dans le manifeste de l’application.

Fichiers d’en-tête

Pour utiliser curl, incluez ces fichiers d’en-tête dans votre 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>
#include <applibs/networking_curl.h>  // required only if using proxy to connect to the internet

Le fichier d’en-tête storage.h est requis uniquement si vous fournissez un ou plusieurs certificats dans le package d’image d’application. L’en-tête deviceauth_curl.h est requis pour effectuer l’authentification mutuelle. L’en-tête networking_curl.h est requis si l’application utilise un proxy pour se connecter à Internet.

Manifeste d’application

Le champ AllowedConnections du manifeste de l’application doit spécifier les hôtes auxquels l’application se connecte. Il doit également contenir le nom de chaque domaine que la connexion peut rencontrer en cas de redirection. Par exemple, et www.microsoft.com sont tous deux microsoft.com requis pour une application qui se connecte à la page d’accueil De Microsoft.

Si l’application utilise l’authentification mutuelle, le champ DeviceAuthentication du manifeste doit inclure l’UUID du locataire Azure Sphere (hérité). Les certificats d’authentification d’appareil sont émis uniquement si le catalogue de l’appareil est lié à un UUID de locataire Azure Sphere (hérité) qui correspond à l’UUID du locataire dans le manifeste de l’application. Cette restriction fournit une défense en profondeur : une application s’exécutant sur un appareil dans un autre catalogue (par exemple, celui d’un autre client ou d’une entité non autorisée) ne peut pas s’authentifier auprès du serveur.

Pendant le développement, vous pouvez trouver l’UUID du locataire hérité en utilisant la commande az sphere catalog show et en extrayant la MigratedCatalogId valeur de l’objet tags .

Si l’application utilise un proxy, le champ ReadNetworkProxyConfig indique si l’application est autorisée à récupérer la configuration du proxy.

Dans l’exemple suivant, le champ AllowedConnections spécifie que l’application se connecte uniquement à www.example.com, le champ DeviceAuthentication spécifie l’UUID du locataire Azure Sphere (hérité), ce qui permet à l’application d’utiliser le certificat d’appareil pour l’authentification mutuelle, et le champ ReadNetworkProxyConfig spécifie que l’application peut récupérer les informations de configuration du proxy.

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

Fonctionnalités prises en charge

Libcurl pour Azure Sphere prend uniquement en charge les protocoles HTTP et HTTPS. En outre, le système d’exploitation Azure Sphere ne prend pas en charge certaines fonctionnalités, telles que les fichiers accessibles en écriture (cookies) ou les sockets UNIX. Les fonctionnalités qui ne seront pas prises en charge dans les futures versions libcurl, telles que la famille mprintf(), ne sont pas disponibles.

Libcurl pour Azure Sphere prend en charge TLS 1.2 et TLS 1.3, et a mis hors service TLS 1.0 et TLS 1.1 en conformité avec la stratégie de sécurité TLS plus large de Microsoft.

Voici les suites de chiffrement prises en charge :

  • 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

Les tentatives d’utilisation d’une version non prise en charge de TLS retournent l’erreur CyaSSL does not support <version>.

Authentification du serveur

Azure Sphere prend en charge l’authentification serveur via libcurl. Le certificat du serveur doit être signé par une autorité de certification approuvée par l’appareil. Pour que libcurl authentifie un serveur, l’application doit fournir le chemin d’accès au fichier d’autorité de certification.

Ajouter des certificats d’autorité de certification au package d’images

Pour utiliser une ou plusieurs autorités de certification, vous devez ajouter les certificats à votre package d’images. Chaque certificat doit être encodé en base 64. L’approche la plus simple consiste à créer un fichier unique qui contient tous les certificats supplémentaires. Le fichier doit avoir l’extension de nom de fichier .pem. Pour ajouter des certificats :

  1. Créez un dossier certs dans le dossier de projet de votre application. Le dossier du projet contient le fichier CMakeLists pour votre application.
  2. Dans le dossier certs, créez un fichier texte avec l’extension .pem, copiez-y chaque certificat et enregistrez le fichier.
  3. Dans le fichier CMakeLists.txt, ajoutez le fichier de certificat au package d’images en tant que fichier de ressources. Par exemple :
azsphere_target_add_image_package(${PROJECT_NAME} RESOURCE_FILES "certs/DigiCertGlobalRootCA.pem")

Le fichier de certificat doit maintenant apparaître dans le dossier certs du package d’images.

Définir des emplacements de certificat

Dans votre application, utilisez les options CURLOPT_CAPATH et CURLOPT_CAINFO pour définir les emplacements des certificats. Appelez Storage_GetAbsolutePathInImagePackage pour récupérer le chemin absolu des certificats dans le package d’images, puis appelez curl_easy_setopt.

CURLOPT_CAPATH définit un dossier par défaut pour les certificats. Par exemple, le code suivant indique à curl de rechercher des certificats dans le dossier certs de l’image :

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

CURLOPT_CAINFO définit un chemin d’accès à un fichier qui contient un ou plusieurs certificats. Curl recherche ce fichier en plus du dossier par défaut défini dans CURLOPT_CAPATH. Par exemple :

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

Ce code indique à curl d’approuver toutes les autorités de certification définies dans le fichier mycertificates.pem, en plus des autorités de certification définies dans le répertoire défini dans CURLOPT_CAPATH.

Authentification mutuelle

L’authentification mutuelle vérifie que le serveur et l’appareil client sont légitimes. Il s’agit d’un processus en plusieurs étapes :

  1. L’application authentifie le serveur à l’aide d’un certificat d’autorité de certification, comme décrit dans Authentification du serveur.
  2. L’application présente un certificat d’authentification client x509 au serveur afin que le serveur puisse authentifier l’appareil.
  3. Le serveur utilise la chaîne de certificats du catalogue Azure Sphere pour vérifier que l’appareil appartient au catalogue.

Une application peut configurer le côté de l’authentification mutuelle de l’appareil de deux manières :

  • Configurez la fonction DeviceAuth_CurlSslFunc Azure Sphere en tant que fonction SSL qui effectue l’authentification.
  • Créez une fonction SSL personnalisée qui appelle la fonction DeviceAuth_SslCtxFunc Azure Sphere pour l’authentification.

Note

Azure Sphere ne prend pas en charge la renégociation SSL/TLS.

Avant d’utiliser l’une ou l’autre des fonctions, vous devez mettre à jour le fichier CMakeLists.txt de votre application afin d’ajouter curl et tlsutils à TARGET_LINK_LIBRARIES :

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

Utiliser DeviceAuth_CurlSslFunc

Le moyen le plus simple d’effectuer l’authentification de l’appareil consiste à configurer DeviceAuth_CurlSslFunc en tant que fonction de rappel pour l’authentification CURL SSL :

// 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;
}

La fonction DeviceAuth_CurlSslFunc récupère la chaîne de certificats pour le catalogue Azure Sphere actuel et configure la connexion curl pour effectuer l’authentification mutuelle. Si l’authentification échoue, la fonction retourne CURLE_SSL_CERTPROBLEM.

Utiliser DeviceAuth_SslCtxFunc

Une application peut également utiliser une fonction de rappel SSL personnalisée qui appelle la fonction DeviceAuth_SslCtxFunc Azure Sphere pour l’authentification.

Votre fonction SSL personnalisée doit appeler DeviceAuth_SslCtxFunc pour effectuer l’authentification, mais peut également effectuer d’autres tâches liées à l’authentification. DeviceAuth_SslCtxFunc retourne une valeur de l’énumération DeviceAuthSslResult , qui fournit des informations détaillées sur l’échec. Par exemple :

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;
    }

Utiliser la chaîne de certificats de catalogue sur votre serveur

Pour effectuer l’authentification mutuelle, le serveur doit être en mesure de vérifier que l’appareil appartient à votre catalogue Azure Sphere et que le catalogue lui-même est légitime. Pour effectuer cette authentification, le serveur a besoin de la chaîne de certificats du catalogue Azure Sphere, qui signe tous vos appareils Azure Sphere :

Pour obtenir la chaîne de certificats de votre catalogue, téléchargez-la dans un fichier .p7b, comme dans l’exemple suivant :

az sphere ca-certificate download-chain --destination CA-cert-chain.p7b

Vous pouvez ensuite utiliser le fichier .p7b sur votre serveur.

Conseils supplémentaires pour l’utilisation de curl

Voici quelques conseils supplémentaires pour utiliser curl dans une application Azure Sphere.

  • Si vous envisagez de stocker du contenu de page en RAM ou flash, gardez à l’esprit que le stockage sur l’appareil Azure Sphere est limité.

  • Pour vous assurer que curl suit les redirections, ajoutez ce qui suit à votre code :

    curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
    
  • Pour ajouter des informations détaillées sur les opérations curl qui peuvent être utiles pendant le débogage :

    curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
    
  • Certains serveurs retournent des erreurs si une requête ne contient pas d’agent utilisateur. Pour définir un agent utilisateur :

    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
    
  • Lorsque vous gérez curl_multi rappels du minuteur, évitez les appels récursifs lorsque le délai d’expiration signalé est de 0 ms, car cela peut entraîner un comportement imprévisible. Au lieu de cela, traitez 0 ms comme 1 ms en déclenchant un EventLoopTimer (les EventLoopTimers de 0 ms sont également récursifs et doivent être évités).

    static int CurlTimerCallback(CURLM *multi, long timeoutMillis, void *unused)
    {
         // A value of -1 means the timer does not need to be started.
         if (timeoutMillis != -1) {
    
             if (timeoutMillis == 0) {
                 // We cannot queue an event for 0ms in the future (the timer never fires)
                 // So defer it very slightly (see https://curl.se/libcurl/c/multi-event.html)
                 timeoutMillis = 1;
             }
    
             // Start a single shot timer with the period as provided by cURL.
             // The timer handler will invoke cURL to process the web transfers.
             const struct timespec timeout = {.tv_sec = timeoutMillis / 1000,
                                              .tv_nsec = (timeoutMillis % 1000) * 1000000};
             SetEventLoopTimerOneShot(curlTimer, &timeout);
         }
    
         return 0;
    }