線上至 Web 服務

Azure 球體 SDK 包含 libcurl 文件庫,供高層級應用程式用來連線和驗證 HTTP 和 HTTPS Web 服務。 伺服器和客戶端驗證皆受支援,因此應用程式可以確認它們與預期的伺服器通訊,並可向伺服器證明其裝置和 Azure 球體目錄合法。 共同驗證 會合併這兩者。

GitHub 上的 Azure 球體樣本復原包括下列曲曲樣本:

雖然在 HTTPS_Curl_Easy 中同步處理伺服器驗證的方法相當簡單,但 Azure 球體應用程式通常應該使用HTTPS_Curl_Multi範例中顯示的較複雜的異步技術,以及以 ep publisher 為基礎的單線程事件導向模式。

libcurl 網站提供 libcurl C API 的詳盡文件和許多範例。 cURL 文件庫與 Azure 球體 SDK 執行時間文檔庫之間的差異如下:

常數名稱
(定義)
cURL 範圍限制 Azure 球體範圍限制
CURLOPT_BUFFERSIZE
(緩衝區大小)
預設值:16 KB 預設值:1536 KB
CURLOPT_UPLOAD_BUFFERSIZE
(上傳緩衝區大小)
預設值:64 KB
最大值:2MB
最低:16 KB
預設值:1536 KB
最大值:64 KB
最低:1536 KB
CURLOPT_HEADERFUNCTION
(完成的 HTTP 標頭傳遞至此函數)
上限:100 KB 最大值:16 KB
CURLOPT_DNS_CACHE_TIMEOUT 預設:60 秒的快取結果
最大值:永遠快取結果
最小值:0 (不要快取結果)
所有值都會覆寫為 0,且不會快取結果。

使用曲曲的應用程式需求

使用捲曲庫的應用程式必須包含適當的頁首檔案,並在 應用程式指令清單中提供 Azure 球體 (舊版) 租使用者 UUID 和因特網主機資訊。

頁首檔案

若要使用 curl,請在應用程式中包含下列頁首檔案:

#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

只有當您在應用程式映像套件中提供一或多個憑證時,才需要儲存。h 頁首檔案。 執行共同驗證需要deviceauth_curl.h 標頭。 如果應用程式使用 Proxy 連線到因特網,則需要networking_curl.h 標頭。

應用程式指令清單

應用程式指令清單的 AllowedConnections 功能變數必須指定應用程式連接的主機。 它也必須包含連線在重新導向時可能會遇到的每個網域名稱。 例如,連線至 Microsoft 首頁的應用程式必須同時使用兩microsoft.comwww.microsoft.com者。

如果應用程式使用共同驗證,指令清單的 DeviceAuthentication 欄位必須包含 Azure 球體 (舊版) 租使用者 UUID。 只有在裝置的目錄連結至 Azure 球體 (舊版) 租使用者 UUID 且符合應用程式指令清單中的租使用者 UUID 時,才會發行裝置驗證憑證。 這項限制可提供深度防禦:在不同型錄裝置上執行的應用程式 (例如,不同客戶或實體的應用程式) 無法向伺服器驗證。

在開發期間,您可以使用 az 球型目錄顯示命令,並從tags物件中擷取MigratedCatalogId值,來尋找舊版租使用者 UUID。

如果應用程式使用 Proxy,ReadNetworkProxyConfig 欄位會指出應用程式是否具有擷取 Proxy 設定的許可權。

在下列範例中, AllowedConnections 功能變數指定應用程式僅能連線至 www.example.comDeviceAuthentication 功能變數指定 Azure 球體 (舊版) 租使用者 UUID,讓應用程式使用裝置憑證進行共同驗證, 而 ReadNetworkProxyConfig 功能變數則指定應用程式可以復原 Proxy 設定資訊。

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

支援的功能

Azure 球體的 Libcurl 僅支援 HTTP 和 HTTPS 通訊協定。 此外,Azure 球體 OS 不支援某些功能,例如可寫檔 (Cookie) 或 UNIX 套接字。 日後版本不支援的功能,例如 mprintf () 系列,無法使用。

Azure 球體的 Libcurl 支援 TLS 1.2 和 TLS 1.3,並且已針對更廣泛的 Microsoft TLS 安全策略,淘汰 TLS 1.0 和 TLS 1.1。

以下是支援的密碼套件:

  • 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

嘗試使用不受支援的 TLS 版本時,會傳回錯誤 CyaSSL does not support <version>

伺服器驗證

Azure 球體透過 libcurl 支援伺服器驗證。 伺服器的憑證必須由證書頒發機構單位 (CA 簽署,) 裝置信任。 若要讓 libcurl 驗證伺服器,應用程式必須提供 CA 檔案的路徑。

將 CA 憑證新增至影像套件

若要使用一或多個 CAs,您必須將憑證新增至影像套件。 每個憑證都必須以 64 為底數編碼。 最簡單的方法是建立包含所有其他憑證的單一檔案。 檔案必須具有 .pem 擴展名。 若要新增憑證:

  1. 在您的應用程式項目資料夾中建立憑證資料夾。 專案資料夾包含您應用程式的 CMakeLists 檔案。
  2. 在 [憑證] 資料夾中,建立擴展名為 .pem 的文本檔、將每個憑證複製到其中,然後儲存盤案。
  3. 在 CMakeLists.txt 檔案中,將憑證檔案新增到圖像套件做為資源檔案。 例如:
azsphere_target_add_image_package(${PROJECT_NAME} RESOURCE_FILES "certs/DigiCertGlobalRootCA.pem")

憑證檔案現在應該會出現在影像套件的憑證資料夾中。

設定憑證位置

在您的應用程式中,使用 CURLOPT_CAPATHCURLOPT_CAINFO 選項來設定憑證的位置。 撥 Storage_GetAbsolutePathInImagePackage 以擷取影像套件中憑證的絕對路徑,然後撥打 curl_easy_setopt

CURLOPT_CAPATH設定憑證的預設資料夾。 例如,下列程序代碼會告知 Curl 在影像的憑證資料夾中尋找憑證:

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

CURLOPT_CAINFO設定包含一或多個憑證之檔案的路徑。 除了 CURLOPT_CAPATH 中設定的預設資料夾之外,Curl 會搜尋此檔案。例如:

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

除了在 CURLOPT_CAPATH 的目錄集定義的 CAs 之外,此程式代碼會告知 curl 信任 mycertificates.pem 檔案中定義的任何 CAs。

共同驗證

共同驗證會驗證伺服器和用戶端裝置都合法。 這是一個多步驟的程式:

  1. 應用程式會使用 CA 憑證驗證伺服器,如 伺服器驗證中所述。
  2. 應用程式會向伺服器顯示 x509 用戶端驗證憑證,讓伺服器可以驗證裝置。
  3. 伺服器會使用 Azure 球體目錄的憑證鏈狀來驗證裝置是否屬於目錄。

應用程式可以透過下列兩種方式之一來設定共同驗證的裝置驗證端:

  • 將 Azure 球 體DeviceAuth_CurlSslFunc 函數設定為執行驗證的 SSL 函數。
  • 建立自定義 SSL 函數,將 Azure 球 體稱為DeviceAuth_SslCtxFunc 函數進行驗證。

注意

Azure 球體不支援 SSL/TLS 重新提要。

使用任一函數之前,您必須先更新 CMakeLists.txt 檔案,讓應用程式將 curl 和 tlsutils 新增至TARGET_LINK_LIBRARIES:

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

使用 DeviceAuth_CurlSslFunc

執行裝置驗證最簡單的方法是將 DeviceAuth_CurlSslFunc 設定為捲曲 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;
}

DeviceAuth_CurlSslFunc函數會擷取目前 Azure 球體目錄的憑證鏈路,並設定卷曲連線以執行共同驗證。 如果驗證失敗,函數會傳回CURLE_SSL_CERTPROBLEM。

使用 DeviceAuth_SslCtxFunc

應用程式也可以使用自定義 SSL 回撥函數來呼叫 Azure 球 體DeviceAuth_SslCtxFunc 函數進行驗證。

您的自定義 SSL 函數必須呼叫 DeviceAuth_SslCtxFunc 來執行驗證,但也可能會執行與驗證相關的其他工作。 DeviceAuth_SslCtxFunc 會傳回列舉的 DeviceAuthSslResult 值,並提供失敗的詳細資訊。 例如:

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

使用伺服器上的型錄憑證鏈

若要執行共同驗證,伺服器必須能夠驗證裝置是否屬於您的 Azure 球體目錄,而且該目錄本身是否合法。 若要執行此驗證,伺服器需要 Azure 球體目錄的憑證鏈,以簽署您所有的 Azure 球體裝置:

若要取得型錄的憑證鏈狀,請將它下載到 .p7b 檔案,如下列範例所示:

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

接著,您可以在伺服器上使用 .p7b 檔案。

使用曲曲的其他秘訣

以下是在 Azure 球體應用程式中使用曲曲的一些額外秘訣。

  • 如果您打算將頁面內容儲存在 RAM 或 Flash 中,請記住 Azure 球體裝置上的儲存空間 有限

  • 若要確保捲曲跟隨重新導向,請將下列專案新增至您的程式代碼:

    curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
    
  • 若要新增有關捲曲作業的詳細資訊,在偵錯期間可能會有説明:

    curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
    
  • 如果要求不包含使用者代理程式,部分伺服器會傳回錯誤。 若要設定使用者代理程式:

    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
    
  • 處理curl_multi定時器回撥時,請避免在回報的逾時為 0ms 時進行週期性通話,因為這可能會導致無法預測的效能。 請改為將 0ms 視為 1ms,方法是觸發 EventLoopTimer (0ms EventLoopTimers 也是遞歸性,應避免) 。

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