Conectarse a servicios web

El SDK de Azure Sphere incluye la biblioteca libcurl, que las aplicaciones de alto nivel pueden usar para conectarse y autenticarse con servicios web HTTP y HTTPS. Tanto la autenticación de servidor como la de cliente son compatibles, para que las aplicaciones puedan comprobar que se comunican con el servidor esperado y puedan demostrar al servidor que su dispositivo y el catálogo de Azure Sphere son legítimos. La autenticación mutua combina ambas.

El repositorio de muestras de Azure Sphere en GitHub incluye las siguientes muestras de rizo:

Aunque el enfoque sincrónico de la autenticación de servidor en HTTPS_Curl_Easy es bastante sencillo, las aplicaciones azure sphere deben usar generalmente la técnica asincrónica más compleja que se muestra en la muestra de HTTPS_Curl_Multi, junto con un patrón basado en epoll basado en un solo subproceso basado en eventos.

El sitio web libcurl proporciona documentación exhaustiva de la API libcurl C y muchos ejemplos. Las diferencias entre la biblioteca cURL y la biblioteca en tiempo de ejecución del SDK de Azure Sphere son las siguientes:

Nombre de constante
(definición)
Límites de intervalos cURL Límites de rango de Azure Sphere
CURLOPT_BUFFERSIZE
(tamaño de búfer)
Valor predeterminado: 16 KB Valor predeterminado: 1536 KB
CURLOPT_UPLOAD_BUFFERSIZE
(tamaño del búfer de carga)
Valor predeterminado: 64 KB
Máximo: 2 MB
Mínimo: 16 KB
Valor predeterminado: 1536 KB
Máximo: 64 KB
Mínimo: 1536 KB
CURLOPT_HEADERFUNCTION
(encabezado HTTP completo pasado a esta función)
Máximo: 100 KB Máximo: 16 KB
CURLOPT_DNS_CACHE_TIMEOUT Valor predeterminado: resultados de la caché durante 60 segundos
Máximo: resultados de caché para siempre
Mínimo: 0 (no almacenar resultados en caché)
Todos los valores se reemplazan en 0 y los resultados no se almacenan en caché.

Requisitos para aplicaciones que usan curl

Las aplicaciones que usan la biblioteca curl deben incluir los archivos de encabezado adecuados y proporcionar información UUID del inquilino de Azure Sphere (heredado) e información de host de Internet en el manifiesto de la aplicación.

Archivos de encabezado

Para usar curl, incluya estos archivos de encabezado en la aplicación:

#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

El archivo de encabezado storage.h solo es necesario si proporciona uno o más certificados en el paquete de imagen de la aplicación. El encabezado deviceauth_curl.h es necesario para realizar autenticación mutua. El encabezado networking_curl.h es necesario si la aplicación está usando un proxy para conectarse a Internet.

Manifiesto de la aplicación

El campo AllowedConnections del manifiesto de la aplicación debe especificar los hosts a los que se conecta la aplicación. También debe contener el nombre de cada dominio que pueda encontrar la conexión si se le redirige. Por ejemplo, ambos microsoft.com y www.microsoft.com son necesarios para una aplicación que se conecta a la página principal de Microsoft.

Si la aplicación usa autenticación mutua, el campo DeviceAuthentication del manifiesto debe incluir el UUID del inquilino de Azure Sphere (heredado). Los certificados de autenticación de dispositivo se emiten solo si el catálogo del dispositivo está vinculado a un UUID del inquilino de Azure Sphere (heredado) que coincida con el UUID del inquilino en el manifiesto de la aplicación. Esta restricción proporciona defensa en profundidad: una aplicación que se ejecuta en un dispositivo en un catálogo diferente (por ejemplo, el de un cliente diferente o una entidad fraudulenta) no puede autenticarse con el servidor.

Durante el desarrollo, se puede encontrar el UUID inquilino heredado mediante el comando az sphere catalog show y la extracción del MigratedCatalogId valor del tags objeto.

Si la aplicación usa un proxy, el campo ReadNetworkProxyConfig indica si la aplicación tiene permiso para recuperar la configuración del proxy.

En el ejemplo siguiente, el campo AllowedConnections especifica que la aplicación se conecta solo a www.example.com, el campo DeviceAuthentication especifica el UUID del inquilino de Azure Sphere (heredado), lo que permite a la aplicación usar el certificado de dispositivo para la autenticación mutua, y el campo ReadNetworkProxyConfig especifica que la aplicación puede volver a procesar la información de configuración del proxy.

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

Funcionalidad compatible

Libcurl para Azure Sphere solo admite los protocolos HTTP y HTTPS. Además, azure Sphere OS no admite algunas funciones, como archivos grabables (cookies) o sockets UNIX. Las características que no serán compatibles en futuras versiones de libcurl, como la familia mprintf(), no están disponibles.

Libcurl para Azure Sphere admite TLS 1.2 y TLS 1.3, y ha retirado TLS 1.0 y TLS 1.1 en alineación con la estrategia de seguridad microsoft TLS más amplia.

Los siguientes son los conjuntos de cifrado compatibles:

  • 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

Los intentos de usar una versión no compatible de TLS devuelven el error CyaSSL does not support <version>.

Autenticación de servidor

Azure Sphere admite la autenticación de servidor a través de libcurl. El certificado del servidor debe estar firmado por una entidad de certificación (CA) en la que confía el dispositivo. Para que libcurl autentique un servidor, la aplicación debe proporcionar la ruta de acceso al archivo de CA.

Agregar certificados de entidad de certificación al paquete de imágenes

Para usar uno o más CA, debe agregar los certificados al paquete de imagen. Cada certificado debe estar codificado en base 64. El enfoque más sencillo es crear un único archivo que contenga todos los certificados adicionales. El archivo debe tener la extensión de nombre de archivo .pem. Para agregar certificados:

  1. Cree una carpeta de certificados en la carpeta de proyecto de la aplicación. La carpeta del proyecto contiene el archivo CMakeLists de la aplicación.
  2. En la carpeta certs, cree un archivo de texto con la extensión .pem, copie cada certificado en él y guarde el archivo.
  3. En el archivo CMakeLists.txt, agregue el archivo de certificado al paquete de imagen como un archivo de recursos. Por ejemplo:
azsphere_target_add_image_package(${PROJECT_NAME} RESOURCE_FILES "certs/DigiCertGlobalRootCA.pem")

El archivo de certificado debería aparecer ahora en la carpeta de certificados del paquete de imágenes.

Establecer ubicaciones de certificado

En la aplicación, use las opciones CURLOPT_CAPATH y CURLOPT_CAINFO para establecer las ubicaciones de los certificados. Llame a Storage_GetAbsolutePathInImagePackage para recuperar la ruta de acceso absoluta a los certificados del paquete de imagen y, a continuación, llame a curl_easy_setopt.

CURLOPT_CAPATH establece una carpeta predeterminada para los certificados. Por ejemplo, el código siguiente indica a Curl que busque certificados en la carpeta de certificados de la imagen:

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

CURLOPT_CAINFO establece una ruta de acceso a un archivo que contiene uno o más certificados. Curl busca en este archivo, además de la carpeta predeterminada establecida en CURLOPT_CAPATH. Por ejemplo:

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

Este código indica a curl que confíe en cualquier CA definida en el archivo mycertificates.pem, además de las CA definidas en el conjunto de directorios de CURLOPT_CAPATH.

Autenticación mutua

La autenticación mutua comprueba que tanto el servidor como el dispositivo cliente sean legítimos. Es un proceso de varios pasos:

  1. La aplicación autentica el servidor mediante un certificado de CA, como se describe en Autenticación de servidor.
  2. La aplicación presenta un certificado de autenticación de cliente x509 al servidor para que el servidor pueda autenticar el dispositivo.
  3. El servidor usa la cadena de certificados del catálogo Azure Sphere para comprobar que el dispositivo pertenece al catálogo.

Una aplicación puede configurar el lado de autenticación de dispositivo de autenticación mutua de dos maneras:

  • Configure la función DeviceAuth_CurlSslFunc Azure Sphere como la función SSL que realiza la autenticación.
  • Cree una función SSL personalizada que llame a la función DeviceAuth_SslCtxFunc Azure Sphere para la autenticación.

Nota

Azure Sphere no admite la renegociación de SSL/TLS.

Antes de usar cualquiera de las funciones, debe actualizar el archivo de CMakeLists.txt de la aplicación para agregar curl y tlsutils a TARGET_LINK_LIBRARIES:

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

Usar DeviceAuth_CurlSslFunc

La forma más sencilla de realizar la autenticación de dispositivo es configurar DeviceAuth_CurlSslFunc como la función de devolución de llamada para la autenticación SSL de 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;
}

La función DeviceAuth_CurlSslFunc recupera la cadena de certificados para el catálogo actual de Azure Sphere y configura la conexión curl para realizar la autenticación mutua. Si se produce un error de autenticación, la función devuelve CURLE_SSL_CERTPROBLEM.

Usar DeviceAuth_SslCtxFunc

Una aplicación también puede usar una función de devolución de llamada SSL personalizada que llame a la función DeviceAuth_SslCtxFunc Azure Sphere para la autenticación.

Su función SSL personalizada debe llamar a DeviceAuth_SslCtxFunc para realizar la autenticación, pero también puede realizar otras tareas relacionadas con la autenticación. DeviceAuth_SslCtxFunc devuelve un valor de la DeviceAuthSslResult enumeración, que proporciona información detallada sobre el error. Por ejemplo:

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 la cadena de certificados del catálogo en el servidor

Para realizar la autenticación mutua, el servidor debe ser capaz de verificar que el dispositivo pertenece a su catálogo de Azure Sphere y que el catálogo en sí es legítimo. Para realizar esta autenticación, el servidor requiere la cadena de certificados del catálogo azure Sphere, que firma todos los dispositivos Azure Sphere:

Para obtener la cadena de certificados para el catálogo, descárguela en un archivo .p7b, como en el ejemplo siguiente:

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

A continuación, puede usar el archivo .p7b en el servidor.

Sugerencias adicionales para usar curl

Estas son algunas sugerencias adicionales para usar curl en una aplicación Azure Sphere.

  • Si planea almacenar contenido de la página en memoria RAM o flash, tenga en cuenta que el almacenamiento en el dispositivo Azure Sphere es limitado.

  • Para asegurarte de que curl sigue redirecciones, agrega lo siguiente al código:

    curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
    
  • Para agregar información detallada sobre las operaciones de curl que puede ser útil durante la depuración:

    curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
    
  • Algunos servidores devuelven errores si una solicitud no contiene un agente de usuario. Para establecer un agente de usuario:

    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
    
  • Al controlar las devoluciones de llamada del temporizador curl_multi, evite las llamadas recursivas cuando el tiempo de espera notificado sea de 0 ms, ya que esto puede provocar un comportamiento impredecible. En su lugar, trate 0ms como 1ms desencadenando un EventLoopTimer (0ms EventLoopTimers también son recursivos y deben evitarse).

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