Подключение к веб-службам

Пакет SDK для Azure Sphere включает библиотеку libcurl, которую высокоуровневые приложения могут использовать для подключения и проверки подлинности с помощью веб-служб HTTP и HTTPS. Проверка подлинности сервера и клиента поддерживается, поэтому приложения могут убедиться, что они обмениваются данными с ожидаемым сервером, и могут доказать серверу, что их устройство и каталог Azure Sphere являются допустимыми. Взаимная проверка подлинности объединяет эти два элемента.

Репозиторий примеров Azure Sphere на GitHub включает следующие примеры curl:

  • HTTPS_Curl_Easy использует синхронный (блокирующий) API для проверки подлинности сервера.
  • HTTPS_Curl_Multi примере используется асинхронный (неблокирующий) API для проверки подлинности сервера.

Хотя синхронный подход к проверке подлинности сервера в HTTPS_Curl_Easy довольно прост, приложениям Azure Sphere, как правило, следует использовать более сложный асинхронный метод, показанный в примере HTTPS_Curl_Multi, а также шаблон на основе однопотоковых событий на основе epoll.

На веб-сайте libcurl представлена подробная документация по API libcurl C и множество примеров. Ниже перечислены различия между библиотекой cURL и библиотекой среды выполнения пакета SDK для Azure Sphere.

Имя константы
(определение)
ограничения диапазона cURL Ограничения диапазона Azure Sphere
CURLOPT_BUFFERSIZE
(размер буфера)
По умолчанию: 16 КБ По умолчанию: 1536 КБ
CURLOPT_UPLOAD_BUFFERSIZE
(размер буфера отправки)
По умолчанию: 64 КБ
Максимум: 2 МБ
Минимум: 16 КБ
По умолчанию: 1536 КБ
Максимум: 64 КБ
Минимум: 1536 КБ
CURLOPT_HEADERFUNCTION
(полный заголовок HTTP, переданный этой функции)
Максимум: 100 КБ Максимум: 16 КБ
CURLOPT_DNS_CACHE_TIMEOUT По умолчанию: кэшировать результаты в течение 60 секунд
Максимум: кэшировать результаты навсегда
Минимум: 0 (не кэшировать результаты)
Все значения переопределяются на 0, а результаты не кэшируются.

Требования для приложений, использующих curl

Приложения, использующие библиотеку curl, должны включать соответствующие файлы заголовков и предоставлять UUID клиента Azure Sphere (устаревшей версии) и сведения об узле интернета в манифесте приложения.

Файлы заголовков

Чтобы использовать 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 требуется только в том случае, если вы предоставляете один или несколько сертификатов в пакете образа приложения. Для выполнения взаимной проверки подлинности требуется заголовок deviceauth_curl.h. Если приложение использует прокси-сервер для подключения к Интернету, требуется заголовок networking_curl.h.

Манифест приложения

В поле AllowedConnections манифеста приложения должны быть указаны узлы, к которым подключается приложение. Он также должен содержать имя каждого домена, с которым может столкнуться подключение при перенаправлении. Например, для приложения, которое подключается к домашней странице Майкрософт, требуется и microsoft.comwww.microsoft.com .

Если приложение использует взаимную проверку подлинности, поле DeviceAuthentication манифеста должно содержать UUID клиента Azure Sphere (устаревшая версия). Сертификаты проверки подлинности устройств выдаются только в том случае, если каталог устройства связан с UUID клиента Azure Sphere (устаревшей версии), который соответствует идентификатору UUID клиента в манифесте приложения. Это ограничение обеспечивает глубинную защиту: приложение, работающее на устройстве в другом каталоге (например, другого клиента или изгоев сущности), не может пройти проверку подлинности на сервере.

Во время разработки можно найти устаревший идентификатор UUID клиента с помощью команды az sphere catalog show и извлечения MigratedCatalogId значения из tags объекта .

Если приложение использует прокси-сервер, поле ReadNetworkProxyConfig указывает, имеет ли приложение разрешение на получение конфигурации прокси-сервера.

В следующем примере поле AllowedConnections указывает, что приложение подключается только к www.example.com, в поле DeviceAuthentication указывается UUID клиента Azure Sphere (устаревшая версия), что позволяет приложению использовать сертификат устройства для взаимной проверки подлинности, а поле ReadNetworkProxyConfig указывает, что приложение может получать сведения о конфигурации прокси-сервера.

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

Поддерживаемые функции

Libcurl для Azure Sphere поддерживает только протоколы HTTP и HTTPS. Кроме того, ОС Azure Sphere не поддерживает некоторые функции, такие как записываемые файлы (файлы cookie) или сокеты UNIX. Функции, которые не будут поддерживаться в будущих выпусках libcurl, например семейство mprintf(), недоступны.

Libcurl для Azure Sphere поддерживает ПРОТОКОЛы TLS 1.2 и TLS 1.3, а также использует протоколы TLS 1.0 и TLS 1.1 в соответствии с более широкой стратегией безопасности Microsoft TLS.

Ниже приведены поддерживаемые наборы шифров:

  • 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. Сертификат сервера должен быть подписан центром сертификации (ЦС), которому доверяет устройство. Для проверки подлинности сервера libcurl приложение должно предоставить путь к файлу ЦС.

Добавление сертификатов ЦС в пакет образа

Чтобы использовать один или несколько центров сертификации, необходимо добавить сертификаты в пакет образа. Каждый сертификат должен быть закодирован в кодировке Base-64. Самый простой подход — создать один файл, содержащий все дополнительные сертификаты. Файл должен иметь расширение PEM-файла. Чтобы добавить сертификаты, выполните следующее:

  1. Создайте папку certs в папке проекта для приложения. Папка проекта содержит файл CMakeLists для приложения.
  2. В папке certs создайте текстовый файл с расширением PEM, скопируйте в него каждый сертификат и сохраните файл.
  3. В файле 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 задает папку по умолчанию для сертификатов. Например, следующий код указывает curl на поиск сертификатов в папке certs на изображении:

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

CURLOPT_CAINFO задает путь к файлу, который содержит один или несколько сертификатов. Curl выполняет поиск в этом файле в дополнение к папке по умолчанию, заданной в CURLOPT_CAPATH. Например:

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

Этот код указывает curl доверять всем ЦС, определенным в файле mycertificates.pem, в дополнение к ЦС, определенным в каталоге, наборе CURLOPT_CAPATH.

Взаимная проверка подлинности

Взаимная проверка подлинности проверяет, что сервер и клиентское устройство являются допустимыми. Это многоэтапный процесс:

  1. Приложение выполняет проверку подлинности сервера с помощью сертификата ЦС, как описано в разделе Проверка подлинности сервера.
  2. Приложение предоставляет серверу сертификат проверки подлинности клиента x509, чтобы сервер смог проверить подлинность устройства.
  3. Сервер использует цепочку сертификатов каталога Azure Sphere для проверки принадлежности устройства к каталогу.

Приложение может настроить сторону проверки подлинности устройства для взаимной проверки подлинности двумя способами:

  • Настройте функцию Azure Sphere DeviceAuth_CurlSslFunc в качестве функции SSL, которая выполняет проверку подлинности.
  • Создайте пользовательскую функцию SSL, которая вызывает функцию Azure Sphere DeviceAuth_SslCtxFunc для проверки подлинности.

Примечание

Azure Sphere не поддерживает повторное согласование 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 в качестве функции обратного вызова для проверки подлинности 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;
}

Функция DeviceAuth_CurlSslFunc извлекает цепочку сертификатов для текущего каталога Azure Sphere и настраивает соединение curl для выполнения взаимной проверки подлинности. Если проверка подлинности завершается ошибкой, функция возвращает CURLE_SSL_CERTPROBLEM.

Использование DeviceAuth_SslCtxFunc

Приложение также может использовать пользовательскую функцию обратного вызова SSL, которая вызывает функцию azure Sphere 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 Sphere и что сам каталог является допустимым. Для выполнения этой проверки подлинности серверу требуется цепочка сертификатов каталога Azure Sphere, которая подписывает все ваши устройства Azure Sphere:

Чтобы получить цепочку сертификатов для каталога, скачайте ее в P7B-файл, как показано в следующем примере:

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

Затем можно использовать P7B-файл на сервере.

Дополнительные советы по использованию curl

Ниже приведены некоторые дополнительные советы по использованию curl в приложении Azure Sphere.

  • Если вы планируете хранить содержимое страницы в ОЗУ или флэш-памяти, помните, что хранилище на устройстве 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 мс, так как это может привести к непредсказуемому поведению. Вместо этого обрабатывайте 0ms как 1 мс путем активации EventLoopTimer (0 мс 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;
    }