Conectar-se aos serviços Web

O SDK do Azure Sphere inclui a biblioteca libcurl, que aplicativos de alto nível podem usar para se conectar e autenticar com serviços Web HTTP e HTTPS. Há suporte para autenticação de servidor e cliente, para que os aplicativos possam verificar se estão se comunicando com o servidor esperado e provar ao servidor que seu dispositivo e o locatário do Azure Sphere são legítimos. A autenticação mútua combina os dois.

O repositório de exemplos do Azure Sphere no GitHub inclui os seguintes exemplos de curl:

Embora a abordagem síncrona para a autenticação do servidor no HTTPS_Curl_Easy seja bastante simples, os aplicativos do Azure Sphere geralmente devem usar a técnica assíncrona mais complexa mostrada no exemplo HTTPS_Curl_Multi, juntamente com um padrão baseado em epoll baseado em um único thread.

O site libcurl fornece uma documentação completa da API de C libcurl e muitos exemplos. As diferenças entre a Biblioteca cURL e a biblioteca de runtime do SDK do Azure Sphere são as seguintes:

Nome constante
(definição)
cURL limites de intervalo Limites de intervalo do Azure Sphere
CURLOPT_BUFFERSIZE
(tamanho do buffer)
Padrão: 16 KB Padrão: 1536 KB
CURLOPT_UPLOAD_BUFFERSIZE
(carregar o tamanho do buffer)
Padrão: 64 KB
Máximo: 2MB
Mínimo: 16 KB
Padrão: 1536 KB
Máximo: 64 KB
Mínimo: 1536 KB
CURLOPT_HEADERFUNCTION
(cabeçalho HTTP completo passado para essa função)
Máximo: 100 KB Máximo: 16 KB
CURLOPT_DNS_CACHE_TIMEOUT Padrão: resultados de cache por 60 segundos
Máximo: resultados de cache para sempre
Mínimo: 0 (não armazenar em cache resultados)
Todos os valores são substituídos a 0 e os resultados não são armazenados em cache.

Requisitos para aplicativos que usam curl

Os aplicativos que usam a biblioteca curl devem incluir os arquivos de cabeçalho apropriados e fornecer informações de locatário e host da Internet no manifesto do aplicativo.

Arquivos de cabeçalho

Para usar o curl, inclua esses arquivos de cabeçalho em seu aplicativo:

#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

O arquivo de cabeçalho storage.h só será necessário se você fornecer um ou mais certificados no pacote de imagem do aplicativo. O cabeçalho deviceauth_curl.h é necessário para executar a autenticação mútua. O cabeçalho networking_curl.h será necessário se o aplicativo estiver usando um proxy para se conectar à Internet.

Manifesto do aplicativo

O campo AllowedConnections do manifesto do aplicativo deve especificar os hosts aos quais o aplicativo se conecta. Ele também deve conter o nome de cada domínio que a conexão pode encontrar se for redirecionada. Por exemplo, ambos microsoft.com e www.microsoft.com são necessários para um aplicativo que se conecta à home page da Microsoft.

Se o aplicativo usar autenticação mútua, o campo DeviceAuthentication do manifesto deverá incluir a ID do locatário do Azure Sphere. Os certificados de autenticação do dispositivo só serão emitidos se a ID do locatário do dispositivo corresponder à ID do locatário no manifesto do aplicativo. Essa restrição fornece uma defesa detalhada: um aplicativo em execução em um dispositivo em um locatário diferente (por exemplo, o de um cliente diferente ou uma entidade desonesto) não pode se autenticar no servidor.

Se o aplicativo usar um proxy, o campo ReadNetworkProxyConfig indicará se o aplicativo tem permissão para recuperar a configuração do proxy.

Durante o desenvolvimento, você pode encontrar a ID do locatário atual do Azure Sphere usando o comando azsphere tenant show-selected .

No exemplo a seguir, o campo AllowedConnections especifica que o aplicativo se conecta apenas ao www.example.com, o campo DeviceAuthentication especifica a ID do locatário do Azure Sphere, permitindo que o aplicativo use o certificado de dispositivo para autenticação mútua e o campo ReadNetworkProxyConfig especifica que o aplicativo pode retreiar informações de configuração de proxy.

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

Funcionalidade com suporte

O Libcurl para Azure Sphere dá suporte apenas aos protocolos HTTP e HTTPS. Além disso, o sistema operacional do Azure Sphere não dá suporte a algumas funcionalidades, como arquivos graváveis (cookies) ou soquetes UNIX. Recursos que não terão suporte em versões libcuris futuras, como a família mprintf(), não estão disponíveis.

O Libcurl para Azure Sphere dá suporte ao TLS 1.2 e ao TLS 1.3 e retirou o TLS 1.0 e o TLS 1.1 em alinhamento com a estratégia de segurança mais ampla do Microsoft TLS.

A seguir estão os pacotes de criptografia com suporte:

  • 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

Tentativas de usar uma versão sem suporte do TLS retornam o erro CyaSSL does not support <version>.

Autenticação de servidor

O Azure Sphere dá suporte à autenticação do servidor por meio do libcurl. O certificado do servidor deve ser assinado por uma AC (Autoridade de Certificado) em que o dispositivo confia. Para que o libcurl autentique um servidor, o aplicativo deve fornecer o caminho para o arquivo de AC.

Adicionar certificados de AC ao pacote de imagens

Para usar um ou mais CAs, você deve adicionar os certificados ao pacote de imagens. Cada certificado deve ser codificado com base 64. A abordagem mais simples é criar um único arquivo que contenha todos os certificados adicionais. O arquivo deve ter a extensão .pem filename. Para adicionar certificados:

  1. Crie uma pasta de certificação na pasta de projeto para seu aplicativo. A pasta do projeto contém o arquivo CMakeLists para seu aplicativo.
  2. Na pasta certs, crie um arquivo de texto com a extensão .pem, copie cada certificado nela e salve o arquivo.
  3. No arquivo CMakeLists.txt, adicione o arquivo de certificado ao pacote de imagem como um arquivo de recurso. Por exemplo:
azsphere_target_add_image_package(${PROJECT_NAME} RESOURCE_FILES "certs/DigiCertGlobalRootCA.pem")

O arquivo de certificado agora deve aparecer na pasta certs no pacote de imagem.

Definir locais de certificado

Em seu aplicativo, use as opções CURLOPT_CAPATH e CURLOPT_CAINFO para definir os locais dos certificados. Chame Storage_GetAbsolutePathInImagePackage para recuperar o caminho absoluto para os certificados no pacote de imagem e, em seguida, chamar curl_easy_setopt.

CURLOPT_CAPATH define uma pasta padrão para os certificados. Por exemplo, o código a seguir informa ao curl para procurar certificados na pasta certs na imagem:

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

CURLOPT_CAINFO define um caminho para um arquivo que contém um ou mais certificados. Curl pesquisa esse arquivo, além da pasta padrão definida em CURLOPT_CAPATH. Por exemplo:

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

Esse código informa ao curl para confiar em todos os CAs definidos no arquivo mycertificates.pem, além dos CAs definidos no diretório definido em CURLOPT_CAPATH.

Autenticação mútua

A autenticação mútua verifica se o servidor e o dispositivo cliente são legítimos. É um processo de várias etapas:

  1. O aplicativo autentica o servidor usando um certificado de AC, conforme descrito na autenticação do servidor.
  2. O aplicativo apresenta um certificado de autenticação do cliente x509 para o servidor para que o servidor possa autenticar o dispositivo.
  3. O servidor usa a cadeia de certificados do locatário do Azure Sphere para verificar se o dispositivo pertence ao locatário.

Um aplicativo pode configurar o lado de autenticação de dispositivo da autenticação mútua de duas maneiras:

  • Configure a função DeviceAuth_CurlSslFunc do Azure Sphere como a função SSL que executa a autenticação.
  • Crie uma função SSL personalizada que chama a função DeviceAuth_SslCtxFunc do Azure Sphere para autenticação.

Nota

O Azure Sphere não dá suporte à renegociação SSL/TLS.

Antes de usar qualquer função, você deve atualizar o arquivo CMakeLists.txt para seu aplicativo para adicionar curl e tlsutils a TARGET_LINK_LIBRARIES:

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

Usar DeviceAuth_CurlSslFunc

A maneira mais simples de executar a autenticação do dispositivo é configurar DeviceAuth_CurlSslFunc como a função de retorno de chamada para autenticação SSL curl:

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

A função DeviceAuth_CurlSslFunc recupera a cadeia de certificados do locatário atual do Azure Sphere e configura a conexão curl para executar a autenticação mútua. Se a autenticação falhar, a função retornará CURLE_SSL_CERTPROBLEM.

Usar DeviceAuth_SslCtxFunc

Um aplicativo também pode usar uma função de retorno de chamada SSL personalizada que chama a função de DeviceAuth_SslCtxFunc do Azure Sphere para autenticação.

Sua função SSL personalizada deve chamar DeviceAuth_SslCtxFunc para executar a autenticação, mas também pode fazer outras tarefas relacionadas à autenticação. DeviceAuth_SslCtxFunc retorna um valor da DeviceAuthSslResult enumeração, que fornece informações detalhadas sobre a falha. Por exemplo:

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

Usar a cadeia de certificados de locatário em seu servidor

Para executar a autenticação mútua, o servidor deve ser capaz de verificar se o dispositivo pertence ao locatário do Azure Sphere e se o locatário em si é legítimo. Para executar essa autenticação, o servidor requer a cadeia de certificados do locatário do Azure Sphere, que assina todos os dispositivos do Azure Sphere:

Para obter a cadeia de certificados do locatário, baixe-a em um arquivo .p7b, como no exemplo a seguir:

azsphere ca-certificate download-chain --destination CA-cert-chain.p7b

Em seguida, você pode usar o arquivo .p7b em seu servidor.

Dicas adicionais para usar curl

Aqui estão algumas dicas adicionais para usar o curl em um aplicativo do Azure Sphere.

  • Se você planeja armazenar conteúdo de página em RAM ou flash, tenha em mente que o armazenamento no dispositivo do Azure Sphere é limitado.

  • Para garantir que o curl siga redirecionamentos, adicione o seguinte ao código:

    curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
    
  • Para adicionar informações verbosas sobre operações de curl que podem ser úteis durante a depuração:

    curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
    
  • Alguns servidores retornam erros se uma solicitação não contiver um agente de usuário. Para definir um agente de usuário:

    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
    
  • Ao lidar com curl_multi retornos de chamada do temporizador, evite chamadas recursivas quando o tempo limite relatado for de 0ms, pois isso pode levar a um comportamento imprevisível. Em vez disso, trate 0ms como 1ms disparando um EventLoopTimer (EventLoopTimers de 0ms também são recursivos e devem ser evitados).

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