Herstellen einer Verbindung mit Webdiensten

Das Azure Sphere SDK enthält die bibliothek libcurl, die allgemeine Anwendungen zum Herstellen einer Verbindung und Authentifizierung mit HTTP- und HTTPS-Webdiensten verwenden können. Sowohl die Server- als auch die Clientauthentifizierung werden unterstützt, damit Anwendungen überprüfen können, ob sie mit dem erwarteten Server kommunizieren und dem Server nachweisen können, dass ihr Gerät und der Azure Sphere-Katalog legitim sind. Bei der gegenseitigen Authentifizierung werden beides kombiniert.

Das Azure Sphere-Beispielrepository auf GitHub enthält die folgenden curl-Beispiele:

  • HTTPS_Curl_Easy verwendet eine synchrone (blockierende) API für die Serverauthentifizierung.
  • HTTPS_Curl_Multi Beispiel verwendet eine asynchrone (nicht blockierende) API für die Serverauthentifizierung.

Obwohl der synchrone Ansatz für die Serverauthentifizierung in HTTPS_Curl_Easy recht einfach ist, sollten Azure Sphere-Anwendungen im Allgemeinen die komplexere asynchrone Technik verwenden, die im HTTPS_Curl_Multi Beispiel gezeigt wird, zusammen mit einem epoll-basierten, ereignisgesteuerten Singlethreadmuster.

Die libcurl-Website bietet eine ausführliche Dokumentation der libcurl C-API und viele Beispiele. Die Unterschiede zwischen der cURL Library und der Azure Sphere SDK-Laufzeitbibliothek sind wie folgt:

Konstantenname
(Definition)
cURL Bereichsgrenzwerte Azure Sphere-Bereichsbeschränkungen
CURLOPT_BUFFERSIZE
(Puffergröße)
Standard: 16 KB Standard: 1536 KB
CURLOPT_UPLOAD_BUFFERSIZE
(Puffergröße hochladen)
Standard: 64 KB
Maximal: 2 MB
Minimum: 16 KB
Standard: 1536 KB
Maximal: 64 KB
Minimum: 1536 KB
CURLOPT_HEADERFUNCTION
(vollständiger HTTP-Header, der an diese Funktion übergeben wird)
Maximal: 100 KB Maximal: 16 KB
CURLOPT_DNS_CACHE_TIMEOUT Standard: Zwischenspeichern der Ergebnisse für 60 Sekunden
Maximum: Zwischenspeichern von Ergebnissen für immer
Minimum: 0 (Ergebnisse nicht zwischenspeichern)
Alle Werte werden auf 0 überschrieben, und die Ergebnisse werden nicht zwischengespeichert.

Anforderungen für Anwendungen, die curl verwenden

Anwendungen, die die curl-Bibliothek verwenden, müssen die entsprechenden Headerdateien enthalten und UUID- und Internethostinformationen des Azure Sphere-Mandanten (Legacy) im Anwendungsmanifest bereitstellen.

Headerdateien

Um curl zu verwenden, schließen Sie die folgenden Headerdateien in Ihre Anwendung ein:

#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

Die Headerdatei storage.h ist nur erforderlich, wenn Sie mindestens ein Zertifikat im Anwendungsimagepaket angeben. Der deviceauth_curl.h-Header ist für die gegenseitige Authentifizierung erforderlich. Der networking_curl.h-Header ist erforderlich, wenn die Anwendung einen Proxy verwendet, um eine Verbindung mit dem Internet herzustellen.

Anwendungsmanifest

Das Feld AllowedConnections des Anwendungsmanifests muss die Hosts angeben, mit denen die Anwendung eine Verbindung herstellt. Sie muss auch den Namen jeder Domäne enthalten, die bei der Umleitung der Verbindung auftreten kann. Beispielsweise sind sowohl als www.microsoft.com auch microsoft.com für eine Anwendung erforderlich, die eine Verbindung mit der Microsoft-Startseite herstellt.

Wenn die Anwendung die gegenseitige Authentifizierung verwendet, muss das Feld DeviceAuthentication des Manifests die Azure Sphere-Mandanten-UUID (Legacy) enthalten. Geräteauthentifizierungszertifikate werden nur ausgestellt, wenn der Gerätekatalog mit einer Azure Sphere-Mandanten-UUID (Legacy) verknüpft ist, die der Mandanten-UUID im Anwendungsmanifest entspricht. Diese Einschränkung bietet eine umfassende Verteidigung: Eine Anwendung, die auf einem Gerät in einem anderen Katalog (z. B. dem eines anderen Kunden oder einer nicht autorisierten Entität) ausgeführt wird, kann sich nicht beim Server authentifizieren.

Während der Entwicklung können Sie die Legacymandanten-UUID ermitteln, indem Sie den Befehl az sphere catalog show verwenden und den MigratedCatalogId Wert aus dem tags Objekt extrahieren.

Wenn die Anwendung einen Proxy verwendet, gibt das Feld ReadNetworkProxyConfig an, ob die Anwendung über die Berechtigung zum Abrufen der Proxykonfiguration verfügt.

Im folgenden Beispiel gibt das Feld AllowedConnections an, dass die Anwendung nur www.example.comeine Verbindung mit herstellt, das DeviceAuthentication-Feld die Azure Sphere-Mandanten-UUID (Legacy) angibt, sodass die Anwendung das Gerätezertifikat für die gegenseitige Authentifizierung verwenden kann, und das Feld ReadNetworkProxyConfig gibt an, dass die Anwendung Proxykonfigurationsinformationen erneut erfassen kann.

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

Unterstützte Funktionen

Libcurl für Azure Sphere unterstützt nur die Protokolle HTTP und HTTPS. Darüber hinaus unterstützt das Azure Sphere-Betriebssystem einige Funktionen nicht, z. B. beschreibbare Dateien (Cookies) oder UNIX-Sockets. Features, die in zukünftigen libcurl-Releases nicht unterstützt werden, z. B. die mprintf() -Familie, sind nicht verfügbar.

Libcurl für Azure Sphere unterstützt TLS 1.2 und TLS 1.3 und hat TLS 1.0 und TLS 1.1 in Übereinstimmung mit der umfassenderen Microsoft TLS-Sicherheitsstrategie eingestellt.

Im Folgenden werden die unterstützten Verschlüsselungssammlungen aufgeführt:

  • 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

Versuche, eine nicht unterstützte Version von TLS zu verwenden, geben den Fehler CyaSSL does not support <version>zurück.

Serverauthentifizierung

Azure Sphere unterstützt die Serverauthentifizierung über libcurl. Das Zertifikat des Servers muss von einer Zertifizierungsstelle signiert werden, der das Gerät vertraut. Damit libcurl einen Server authentifizieren kann, muss die Anwendung den Pfad zur Zertifizierungsstellendatei angeben.

Hinzufügen von Zertifizierungsstellenzertifikaten zum Imagepaket

Um eine oder mehrere Zertifizierungsstellen zu verwenden, müssen Sie die Zertifikate ihrem Imagepaket hinzufügen. Jedes Zertifikat muss Base64-codiert sein. Der einfachste Ansatz besteht darin, eine einzelne Datei zu erstellen, die alle zusätzlichen Zertifikate enthält. Die Datei muss die Dateierweiterung PEM aufweisen. So fügen Sie Zertifikate hinzu:

  1. Erstellen Sie einen Ordner certs im Projektordner für Ihre Anwendung. Der Projektordner enthält die CMakeLists-Datei für Ihre Anwendung.
  2. Erstellen Sie im Ordner certs eine Textdatei mit der Erweiterung .pem, kopieren Sie jedes Zertifikat hinein, und speichern Sie die Datei.
  3. Fügen Sie in der CMakeLists.txt-Datei die Zertifikatdatei dem Imagepaket als Ressourcendatei hinzu. Zum Beispiel:
azsphere_target_add_image_package(${PROJECT_NAME} RESOURCE_FILES "certs/DigiCertGlobalRootCA.pem")

Die Zertifikatdatei sollte nun im Ordner certs im Imagepaket angezeigt werden.

Festlegen von Zertifikatspeicherorten

Verwenden Sie in Ihrer Anwendung die Optionen CURLOPT_CAPATH und CURLOPT_CAINFO , um die Speicherorte der Zertifikate festzulegen. Rufen Sie Storage_GetAbsolutePathInImagePackage auf, um den absoluten Pfad zu den Zertifikaten im Imagepaket abzurufen, und rufen Sie dann curl_easy_setopt auf.

CURLOPT_CAPATH legt einen Standardordner für die Zertifikate fest. Der folgende Code weist curl beispielsweise an, im Ordner certs in der Abbildung nach Zertifikaten zu suchen:

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

CURLOPT_CAINFO legt einen Pfad zu einer Datei fest, die mindestens ein Zertifikat enthält. Curl durchsucht diese Datei zusätzlich zum Standardordner, der in CURLOPT_CAPATH festgelegt ist. Zum Beispiel:

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

Dieser Code weist curl an, allen Zertifizierungsstellen zu vertrauen, die in der Datei mycertificates.pem definiert sind, zusätzlich zu den Zertifizierungsstellen, die in dem in CURLOPT_CAPATH festgelegten Verzeichnis definiert sind.

Gegenseitige Authentifizierung

Die gegenseitige Authentifizierung überprüft, ob sowohl der Server als auch das Clientgerät legitim sind. Es handelt sich um einen mehrstufigen Prozess:

  1. Die Anwendung authentifiziert den Server mithilfe eines Zertifizierungsstellenzertifikats, wie unter Serverauthentifizierung beschrieben.
  2. Die Anwendung stellt dem Server ein x509-Clientauthentifizierungszertifikat vor, damit der Server das Gerät authentifizieren kann.
  3. Der Server verwendet die Zertifikatkette des Azure Sphere-Katalogs, um zu überprüfen, ob das Gerät zum Katalog gehört.

Eine Anwendung kann die Geräteauthentifizierungsseite der gegenseitigen Authentifizierung auf zwei Arten einrichten:

  • Konfigurieren Sie die Azure Sphere-DeviceAuth_CurlSslFunc-Funktion als SSL-Funktion, die die Authentifizierung ausführt.
  • Erstellen Sie eine benutzerdefinierte SSL-Funktion, die die Azure Sphere-DeviceAuth_SslCtxFunc-Funktion für die Authentifizierung aufruft.

Hinweis

Azure Sphere unterstützt keine SSL-/TLS-Neuverhandlung.

Bevor Sie eine der funktionen verwenden, müssen Sie die CMakeLists.txt-Datei für Ihre Anwendung aktualisieren, um curl und tlsutils TARGET_LINK_LIBRARIES hinzuzufügen:

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

Verwenden von DeviceAuth_CurlSslFunc

Die einfachste Möglichkeit zum Durchführen der Geräteauthentifizierung besteht darin, DeviceAuth_CurlSslFunc als Rückruffunktion für die curl-SSL-Authentifizierung zu konfigurieren:

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

Die DeviceAuth_CurlSslFunc-Funktion ruft die Zertifikatkette für den aktuellen Azure Sphere-Katalog ab und richtet die curl-Verbindung für die gegenseitige Authentifizierung ein. Wenn die Authentifizierung fehlschlägt, gibt die Funktion CURLE_SSL_CERTPROBLEM zurück.

Verwenden von DeviceAuth_SslCtxFunc

Eine Anwendung kann auch eine benutzerdefinierte SSL-Rückruffunktion verwenden, die die Azure Sphere DeviceAuth_SslCtxFunc-Funktion für die Authentifizierung aufruft.

Ihre benutzerdefinierte SSL-Funktion muss DeviceAuth_SslCtxFunc aufrufen, um die Authentifizierung durchzuführen, kann aber auch andere Aufgaben im Zusammenhang mit der Authentifizierung ausführen. DeviceAuth_SslCtxFunc gibt einen Wert der -Enumeration zurück, der DeviceAuthSslResult detaillierte Informationen zum Fehler bereitstellt. Zum Beispiel:

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

Verwenden der Katalogzertifikatkette auf Ihrem Server

Für die gegenseitige Authentifizierung muss der Server in der Lage sein, zu überprüfen, ob das Gerät zu Ihrem Azure Sphere-Katalog gehört und ob der Katalog selbst legitim ist. Für diese Authentifizierung benötigt der Server die Zertifikatkette des Azure Sphere-Katalogs, die alle Ihre Azure Sphere-Geräte signiert:

Um die Zertifikatkette für Ihren Katalog abzurufen, laden Sie ihn wie im folgenden Beispiel in eine P7B-Datei herunter:

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

Anschließend können Sie die P7B-Datei auf Ihrem Server verwenden.

Zusätzliche Tipps für die Verwendung von curl

Hier finden Sie einige zusätzliche Tipps für die Verwendung von curl in einer Azure Sphere-Anwendung.

  • Wenn Sie Planen, Seiteninhalte im RAM oder Flash zu speichern, beachten Sie, dass der Speicher auf dem Azure Sphere-Gerät begrenzt ist.

  • Um sicherzustellen, dass curl Umleitungen folgt, fügen Sie Dem Code Folgendes hinzu:

    curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
    
  • So fügen Sie ausführliche Informationen zu curl-Vorgängen hinzu, die während des Debuggens hilfreich sein können:

    curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
    
  • Einige Server geben Fehler zurück, wenn eine Anforderung keinen Benutzer-Agent enthält. So legen Sie einen Benutzer-Agent fest:

    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
    
  • Vermeiden Sie bei der Behandlung von curl_multi Timerrückrufen rekursive Aufrufe, wenn das gemeldete Timeout 0 ms beträgt, da dies zu unvorhersehbarem Verhalten führen kann. Behandeln Sie stattdessen 0ms als 1 ms, indem Sie einen EventLoopTimer auslösen (0 ms EventLoopTimer sind auch rekursiv und sollten vermieden werden).

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