Web サービスへの接続
重要
これは Azure Sphere (レガシ) のドキュメントです。 Azure Sphere (レガシ) は 2027 年 9 月 27 日に 再提供されておりユーザーは現時点で Azure Sphere (統合) に移行する必要があります。 TOC の上にある Version セレクターを使用して、Azure Sphere (統合) のドキュメントを表示します。
Azure Sphere SDK には、高度なアプリケーションが HTTP/HTTPS Web サービスに接続して認証するために使用できる libcurl ライブラリが含まれています。 サーバー認証とクライアント認証の両方がサポートされているため、アプリケーションでは、想定されるサーバーと通信していることを確認し、正当なデバイスと Azure Sphere テナントであるとサーバーに証明できます。 相互認証ではこの 2 つを結合します。
GitHub 上の Azure Sphere サンプル リポジトリには、次の curl のサンプルが含まれています。
- HTTPS_Curl_Easy では、サーバー認証に同期 (ブロック) API が使用されます。
- HTTPS_Curl_Multi sample では、サーバー認証に非同期 (非ブロック) API が使用されます。
HTTPS_Curl_Easy でのサーバー認証に対する同期アプローチはとてもシンプルですが、Azure Sphere アプリケーションでは、通常、HTTPS_Curl_Multi サンプルに示されているさらに複雑な非同期手法を、epoll ベースのシングルスレッド イベント駆動パターンと共に使用する必要があります。
libcurl Web サイトに、libcurl C API の詳細なドキュメントと多数の例が提供されています。 cURL ライブラリと Azure Sphere SDK ランタイム ライブラリの違いは次のとおりです。
定数名 (定義) |
cURL 範囲の制限 | Azure Sphere の範囲の制限 |
---|---|---|
CURLOPT_BUFFERSIZE (バッファー サイズ) |
既定値: 16 KB | 既定値: 1536 KB |
CURLOPT_UPLOAD_BUFFERSIZE (アップロード バッファー サイズ) |
既定値: 64 KB 最大: 2 MB 最小: 16 KB |
既定値: 1536 KB 最大: 64 KB 最小: 1536 KB |
CURLOPT_HEADERFUNCTION (この関数に渡される完全な HTTP ヘッダー) |
最大: 100 KB | 最大: 16 KB |
CURLOPT_DNS_CACHE_TIMEOUT | 既定値: 結果を 60 秒間キャッシュする 最大: キャッシュの結果は永久に 最小値: 0 (結果をキャッシュしない) |
すべての値は 0 にオーバーライドされ、結果はキャッシュされません。 |
curl を使用するアプリケーションの要件
curl ライブラリを使用するアプリケーションは、適切なヘッダー ファイルを含み、アプリケーション マニフェストにテナントとインターネット ホストの情報を提供する必要があります。
ヘッダー ファイル
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
storage.h ヘッダー ファイルは、アプリケーションのイメージ パッケージに 1 つまたは複数の証明書を指定する場合にのみ必要です。 相互認証を実行するには、deviceauth_curl.h ヘッダーが必要です。 アプリケーションがプロキシを使用してインターネットに接続している場合は、networking_curl.h ヘッダーが必要です。
アプリケーション マニフェスト
アプリケーション マニフェストの AllowedConnections フィールドには、アプリケーションの接続先のホストを指定する必要があります。 また、リダイレクトされた場合に接続先になる可能性がある各ドメインの名前も含める必要があります。 たとえば、Microsoft のホーム ページに接続するアプリケーションでは、microsoft.com
と www.microsoft.com
の両方が必要です。
アプリケーションで相互認証を使用する場合は、マニフェストの DeviceAuthentication フィールドに Azure Sphere テナント ID を含める必要があります。 デバイス認証証明書は、デバイスのテナント ID がアプリケーション マニフェストのテナント ID と一致する場合にのみ発行されます。 この制限で多層防御が実現します。別のテナントのデバイスで実行されているアプリケーション (たとえば、別の顧客または不正なエンティティのもの) は、サーバーに対して認証できません。
アプリケーションがプロキシを使用する場合、 ReadNetworkProxyConfig フィールドは、アプリケーションがプロキシ構成を取得するアクセス許可を持っているかどうかを示します。
開発中は、 azsphere tenant show-selected コマンドを使用して、現在の Azure Sphere テナントの ID を確認できます。
次の例では、 AllowedConnections フィールドは、アプリケーションが www.example.com
にのみ接続することを指定し、 DeviceAuthentication フィールドは Azure Sphere テナント ID を指定し、アプリケーションが相互認証にデバイス証明書を使用できるようにし、 ReadNetworkProxyConfig フィールドは、アプリケーションがプロキシ構成情報を取得できることを指定します。
"Capabilities": {
"AllowedConnections": [ "www.example.com" ],
"Gpio": [],
"Uart": [],
"WifiConfig": false,
"DeviceAuthentication": "00000000-0000-0000-0000-000000000000",
"ReadNetworkProxyConfig": true
}
サポートされる機能
Azure Sphere 用の Libcurl では、HTTP プロトコルと HTTPS プロトコルのみがサポートされます。 さらに、Azure Sphere OS では、書き込み可能ファイル (Cookie) や UNIX ソケットなどの一部の機能はサポートされません。 今後の libcurl リリースでサポートされる予定がない機能 (mprintf() ファミリなど) は使用できません。
Libcurl for Azure Sphere では 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 Sphere は、libcurl を使用したサーバー認証をサポートしています。 デバイスで信頼される証明機関 (CA) によって、サーバーの証明書が署名されている必要があります。 libcurl のサーバー認証では、アプリケーションで CA ファイルへのパスを指定する必要があります。
イメージ パッケージに CA 証明書を追加する
1 つ以上の CA を使用するには、イメージ パッケージに証明書を追加する必要があります。 各証明書は、Base 64 でエンコードされている必要があります。 最も簡単な方法は、すべての追加証明書を含む 1 つのファイルを作成することです。 このファイルには、ファイル名拡張子 .pem が必要です。 証明書を追加するには:
- アプリケーションのプロジェクト フォルダー内に certs フォルダーを作成します。 プロジェクト フォルダーには、アプリケーションの CMakeLists ファイルがあります。
- certs フォルダー内に、拡張子 .pem のテキスト ファイルを作成、各証明書をその中にコピーした後、ファイルを保存します。
- CMakeLists.txt ファイルで、証明書ファイルをリソース ファイルとしてイメージ パッケージに追加します。 次に例を示します。
azsphere_target_add_image_package(${PROJECT_NAME} RESOURCE_FILES "certs/DigiCertGlobalRootCA.pem")
これで、イメージ パッケージ内の certs フォルダーに証明書ファイルが表示されます。
証明書の場所を設定する
アプリケーションで、CURLOPT_CAPATH および CURLOPT_CAINFO オプションを使用して、証明書の場所を設定します。 Storage_GetAbsolutePathInImagePackage を呼び出してイメージ パッケージ内の証明書への絶対パスを取得してから、curl_easy_setopt を呼び出します。
CURLOPT_CAPATH では、証明書の既定のフォルダーを設定します。 たとえば、次のコードは、イメージ内の certs フォルダーで証明書を検索するように curl に指示します。
char *path = Storage_GetAbsolutePathInImagePackage("certs");
curl_easy_setopt(curl_handle, CURLOPT_CAPATH, path);
CURLOPT_CAINFO では、1 つ以上の証明書を含むファイルへのパスを設定します。 curl は、CURLOPT_CAPATH に設定した既定のフォルダーに加えて、このファイルを検索します。次に例を示します。
char *path = Storage_GetAbsolutePathInImagePackage("CAs/mycertificates.pem");
curl_easy_setopt(curl_handle, CURLOPT_CAINFO, path);
このコードでは、CURLOPT_CAPATH に設定されたディレクトリに定義されている CA に加え、mycertificates.pem ファイルに定義されているすべての CA も信頼するように curl に通知します。
相互認証
相互認証では、サーバーとクライアント デバイスの両方が正当であることが検証されます。 次のようなマルチステップのプロセスです。
- 「サーバー認証」で説明されているように、アプリケーションで CA 証明書を使用してサーバーを認証します。
- サーバーがデバイスを認証できるように、アプリケーションからサーバーに x509 クライアント認証証明書を提示します。
- サーバーでは、Azure Sphere テナントの証明書チェーンを使用し、デバイスがテナントに属することを確認します。
アプリケーションでは、次の 2 つの方法のいずれかで、相互認証のデバイス認証側を設定できます。
- 認証を実行する SSL 関数として、Azure Sphere DeviceAuth_CurlSslFunc 関数を構成します。
- 認証のために Azure Sphere DeviceAuth_SslCtxFunc 関数を呼び出すカスタム SSL 関数を作成します。
Note
Azure Sphere では、SSL/TLS 再ネゴシエーションがサポートされていません。
いずれかの関数を使用する前に、アプリケーションの CMakeLists.txt ファイルを更新して TARGET_LINK_LIBRARIES に curl と tlsutils を追加する必要があります。
TARGET_LINK_LIBRARIES(${PROJECT_NAME} applibs pthread gcc_s c curl tlsutils)
DeviceAuth_CurlSslFunc を使用する
デバイス認証を実行する最も簡単な方法は、curl SSL 認証のためのコールバック関数として DeviceAuth_CurlSslFunc を構成することです。
// 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 Sphere テナントの証明書チェーンを取得し、相互認証を実行するように curl 接続を設定します。 認証が失敗した場合、この関数では CURLE_SSL_CERTPROBLEM が返されます。
DeviceAuth_SslCtxFunc を使用する
アプリケーションでは、認証のために Azure Sphere DeviceAuth_SslCtxFunc 関数を呼び出すカスタム SSL コールバック関数も使用できます。
カスタム 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 Sphere テナントに属することとテナント自体が合法であることをサーバーで確認できる必要があります。 この認証を実行するには、あらゆる Azure Sphere デバイスに署名する Azure Sphere テナントの証明書チェーンがサーバーに必要です。
テナントの証明書チェーンを取得するには、次の例のように .p7b ファイルにダウンロードします。
azsphere ca-certificate download-chain --destination CA-cert-chain.p7b
その後、自分のザーバーで .p7b ファイルを使用できます。
curl を使用するためのその他のヒント
Azure Sphere アプリケーションで curl を使用するためのその他のヒントを次に示します。
RAM またはフラッシュにページのコンテンツを格納する予定の場合は、Azure Sphere デバイス上のストレージは限られていることに留意してください。
curl が確実にリダイレクトに従うようにするには、コードに次を追加します。
curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
デバッグ中に役立つ可能性がある curl の操作の詳細情報を追加するには:
curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
一部のサーバーでは、ユーザー エージェントが要求に含まれていない場合、エラーが返ります。 ユーザー エージェントを設定するには:
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
タイマー コールバックcurl_multi処理する場合は、報告されたタイムアウトが 0 ミリ秒のときに再帰的な呼び出しを避けてください。これにより、予期しない動作が発生する可能性があります。 代わりに、EventLoopTimer をトリガーして 0 ミリ秒を 1 ミリ秒として扱います (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; }