웹 서비스에 연결

Azure Sphere SDK에는 고급 애플리케이션이 HTTP 및 HTTPS 웹 서비스에 연결하고 인증하는 데 사용할 수 있는 libcurl 라이브러리가 포함되어 있습니다. 서버 및 클라이언트 인증이 모두 지원되므로 애플리케이션이 예상 서버와 통신하고 있는지 확인하고 해당 디바이스 및 Azure Sphere 카탈로그가 합법적인지 서버에 증명할 수 있습니다. 상호 인증 은 두 가지를 결합합니다.

GitHub의 Azure Sphere 샘플 리포지토리에는 다음 curl 샘플이 포함되어 있습니다.

HTTPS_Curl_Easy 서버 인증에 대한 동기 접근 방식은 매우 간단하지만 Azure Sphere 애플리케이션은 일반적으로 HTTPS_Curl_Multi 샘플에 표시된 보다 복잡한 비동기 기술과 epoll 기반 단일 스레드 이벤트 기반 패턴을 사용해야 합니다.

libcurl 웹 사이트는libcurl C API 및 많은 예제에 대한 철저한 설명서를 제공합니다. cURL 라이브러리와 Azure Sphere SDK 런타임 라이브러리 간의 차이점은 다음과 같습니다.

상수 이름
(정의)
cURL 범위 제한 Azure Sphere 범위 제한
CURLOPT_BUFFERSIZE
(버퍼 크기)
기본값: 16KB 기본값: 1536KB
CURLOPT_UPLOAD_BUFFERSIZE
(업로드 버퍼 크기)
기본값: 64KB
최대: 2MB
최소: 16KB
기본값: 1536KB
최대: 64KB
최소: 1536KB
CURLOPT_HEADERFUNCTION
(이 함수에 전달된 전체 HTTP 헤더)
최대: 100KB 최대: 16KB
CURLOPT_DNS_CACHE_TIMEOUT 기본값: 60초 동안 캐시 결과
최대: 캐시 결과 영원히
최소: 0(결과를 캐시하지 않음)
모든 값은 0으로 재정의되고 결과는 캐시되지 않습니다.

curl을 사용하는 애플리케이션에 대한 요구 사항

curl 라이브러리를 사용하는 애플리케이션은 적절한 헤더 파일을 포함하고 애플리케이션 매니페스트에 Azure Sphere(레거시) 테넌트 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

storage.h 헤더 파일은 애플리케이션 이미지 패키지에 하나 이상의 인증서를 제공하는 경우에만 필요합니다. 상호 인증을 수행하려면 deviceauth_curl.h 헤더가 필요합니다. 애플리케이션이 프록시를 사용하여 인터넷에 연결하는 경우 networking_curl.h 헤더가 필요합니다.

애플리케이션 매니페스트

애플리케이션 매니페스트의 AllowedConnections 필드는 애플리케이션이 연결할 호스트를 지정해야 합니다. 또한 리디렉션될 경우 연결이 발생할 수 있는 각 도메인의 이름도 포함해야 합니다. 예를 들어 microsoft.comwww.microsoft.com 는 모두 Microsoft 홈페이지에 연결하는 애플리케이션에 필요합니다.

애플리케이션이 상호 인증을 사용하는 경우 매니페스트의 DeviceAuthentication 필드에는 Azure Sphere(레거시) 테넌트 UUID가 포함되어야 합니다. 디바이스 인증 인증서는 디바이스 카탈로그가 애플리케이션 매니페스트의 테넌트 UUID와 일치하는 Azure Sphere(레거시) 테넌트 UUID에 연결된 경우에만 발급됩니다. 이 제한은 심층적인 방어를 제공합니다. 다른 카탈로그의 디바이스에서 실행되는 애플리케이션(예: 다른 고객 또는 불량 엔터티의 애플리케이션)은 서버에 인증할 수 없습니다.

개발하는 동안 az sphere catalog show 명령을 사용하고 개체에서 값을 추출하여 레거시 테넌트 UUID를 MigratedCatalogIdtags 찾을 수 있습니다.

애플리케이션에서 프록시를 사용하는 경우 ReadNetworkProxyConfig 필드는 애플리케이션에 프록시 구성을 검색할 수 있는 권한이 있는지 여부를 나타냅니다.

다음 예제에서 AllowedConnections 필드는 애플리케이션이 에만 www.example.com연결되도록 지정하고 , DeviceAuthentication 필드는 Azure Sphere(레거시) 테넌트 UUID를 지정하여 애플리케이션이 상호 인증에 디바이스 인증서를 사용할 수 있도록 하고 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는 쓰기 가능한 파일(쿠키) 또는 UNIX 소켓과 같은 일부 기능을 지원하지 않습니다. mprintf() 제품군과 같은 향후 libcurl 릴리스에서 지원되지 않는 기능은 사용할 수 없습니다.

Azure Sphere용 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 Sphere는 libcurl을 통해 서버 인증을 지원합니다. 서버의 인증서는 디바이스가 신뢰하는 CA(인증 기관)에서 서명해야 합니다. libcurl이 서버를 인증하려면 애플리케이션이 CA 파일의 경로를 제공해야 합니다.

이미지 패키지에 CA 인증서 추가

하나 이상의 CA를 사용하려면 이미지 패키지에 인증서를 추가해야 합니다. 각 인증서는 base-64로 인코딩되어야 합니다. 가장 간단한 방법은 모든 추가 인증서를 포함하는 단일 파일을 만드는 것입니다. 파일에는 .pem 파일 이름 확장명이 있어야 합니다. 인증서를 추가하려면 다음을 수행합니다.

  1. 애플리케이션의 프로젝트 폴더에 인증서 폴더를 만듭니다. 프로젝트 폴더에는 애플리케이션에 대한 CMakeLists 파일이 포함되어 있습니다.
  2. certs 폴더에서 .pem 확장명의 텍스트 파일을 만들고 각 인증서를 복사한 다음 파일을 저장합니다.
  3. CMakeLists.txt 파일에서 이미지 패키지에 인증서 파일을 리소스 파일로 추가합니다. 예를 들어:
azsphere_target_add_image_package(${PROJECT_NAME} RESOURCE_FILES "certs/DigiCertGlobalRootCA.pem")

이제 인증서 파일이 이미지 패키지의 certs 폴더에 표시됩니다.

인증서 위치 설정

애플리케이션에서 CURLOPT_CAPATHCURLOPT_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);

이 코드는 CURLOPT_CAPATH 설정된 디렉터리에 정의된 CA 외에도 mycertificates.pem 파일에 정의된 모든 CA를 신뢰하도록 curl에 지시합니다.

상호 인증

상호 인증은 서버와 클라이언트 디바이스가 모두 합법적인지 확인합니다. 다단계 프로세스입니다.

  1. 애플리케이션은 서버 인증에 설명된 대로 CA 인증서를 사용하여 서버를 인증합니다.
  2. 애플리케이션은 서버가 디바이스를 인증할 수 있도록 서버에 x509 클라이언트 인증 인증서를 제공합니다.
  3. 서버는 Azure Sphere 카탈로그의 인증서 체인을 사용하여 디바이스가 카탈로그에 속하는지 확인합니다.

애플리케이션은 다음 두 가지 방법 중 하나로 상호 인증의 디바이스 인증 측면을 설정할 수 있습니다.

  • Azure Sphere DeviceAuth_CurlSslFunc 함수를 인증을 수행하는 SSL 함수로 구성합니다.
  • 인증을 위해 Azure Sphere DeviceAuth_SslCtxFunc 함수를 호출하는 사용자 지정 SSL 함수를 만듭니다.

참고

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 사용

디바이스 인증을 수행하는 가장 간단한 방법은 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 파일에 다운로드합니다.

az sphere 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 타이머 콜백을 처리할 때는 보고된 시간 제한이 0ms일 때 재귀 호출을 피해야 합니다. 이는 예측할 수 없는 동작으로 이어질 수 있으므로 입니다. 대신 EventLoopTimer를 트리거하여 0ms를 1ms로 처리합니다(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;
    }