HTTP 요청 서명

이 자습서에서는 HMAC 서명을 사용하여 HTTP 요청에 서명하는 방법을 알아봅니다.

참고 항목

Azure SDK를 사용하는 것이 좋습니다. 여기에 설명된 접근 방식은 어떤 이유로든 Azure SDK를 사용할 수 없는 경우에 대한 대체 옵션입니다.

필수 조건

시작하기 전에 다음을 확인해야 합니다.

C#에 HTTP 요청 서명

액세스 키 인증은 공유 비밀 키를 사용하여 각 HTTP 요청에 대해 HMAC 서명을 생성합니다. 이 서명은 SHA256 알고리즘을 사용하여 생성되며 HMAC-SHA256 체계를 사용하여 Authorization 헤더에 전송됩니다. 예시:

Authorization: "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=<hmac-sha256-signature>"

hmac-sha256-signature은 다음으로 구성됩니다.

  • HTTP 동사(예: GET 또는 PUT )
  • HTTP 요청 경로
  • x-ms-date
  • Host
  • x-ms-content-sha256

설정

다음 단계에서는 인증 헤더를 생성하는 방법을 설명합니다.

새 C# 애플리케이션 만들기

콘솔 창(예: cmd, PowerShell 또는 Bash)에서 dotnet new 명령을 사용하여 SignHmacTutorial이라는 새 콘솔 앱을 만듭니다. 이 명령은 Program.cs라는 원본 파일 하나만 들어 있는 간단한 "Hello World" C# 프로젝트를 만듭니다.

dotnet new console -o SignHmacTutorial

새로 만든 앱 폴더로 디렉터리를 변경합니다. dotnet build 명령을 사용하여 애플리케이션을 컴파일합니다.

cd SignHmacTutorial
dotnet build

패키지 설치

본문 직렬화에 사용되는 패키지 Newtonsoft.Json을 설치합니다.

dotnet add package Newtonsoft.Json

비동기 코드를 지원하도록 Main 메서드 선언을 업데이트합니다. 시작하려면 다음 코드를 사용합니다.

using System;
using System.Globalization;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace SignHmacTutorial
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("Azure Communication Services - Sign an HTTP request Tutorial");
            // Tutorial code goes here.
        }
    }
}

요청 메시지 만들기

이 예제에서는 Communication Services Authentication API(버전 2021-03-07)를 사용하여 새 ID를 만들도록 요청에 서명합니다.

Main 메서드에 다음 코드를 추가합니다.

string resourceEndpoint = "resourceEndpoint";
// Create a uri you are going to call.
var requestUri = new Uri($"{resourceEndpoint}/identities?api-version=2021-03-07");
// Endpoint identities?api-version=2021-03-07 accepts list of scopes as a body
var body = new
    {
        createTokenWithScopes = new[] { "chat" }
    };

var serializedBody = JsonConvert.SerializeObject(body);

var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUri)
{
    Content = new StringContent(serializedBody, Encoding.UTF8, "application/json")
};

resourceEndpoint를 실제 리소스 엔드포인트 값으로 바꿉니다.

콘텐츠 해시 만들기

콘텐츠 해시는 HMAC 서명의 일부입니다. 다음 코드를 추가하여 콘텐츠 해시를 컴퓨팅합니다. 이 메서드를 Main 메서드 아래 Progam.cs에 추가할 수 있습니다.

static string ComputeContentHash(string content)
{
    using var sha256 = SHA256.Create();
    byte[] hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(content));
    return Convert.ToBase64String(hashedBytes);
}

서명 컴퓨팅

다음 코드를 사용하여 HMAC 서명을 계산하는 메서드를 만듭니다.

static string ComputeSignature(string stringToSign)
{
    string secret = "resourceAccessKey";
    using var hmacsha256 = new HMACSHA256(Convert.FromBase64String(secret));
    var bytes = Encoding.UTF8.GetBytes(stringToSign);
    var hashedBytes = hmacsha256.ComputeHash(bytes);
    return Convert.ToBase64String(hashedBytes);
}

resourceAccessKey를 실제 Communication Services 리소스의 액세스 키로 바꿉니다.

권한 부여 헤더 문자열 만들기

이제 권한 부여 헤더에 추가할 문자열을 구성합니다.

  1. 서명할 헤더의 값을 준비합니다.
    1. 협정 세계시(UTC) 시간대를 사용하여 현재 타임스탬프를 지정합니다.
    2. 요청 기관(DNS 호스트 이름 또는 IP 주소 및 포트 번호)을 가져옵니다.
    3. 콘텐츠 해시를 계산합니다.
  2. 서명할 문자열을 준비합니다.
  3. 서명을 계산합니다.
  4. 권한 부여 헤더에 사용되는 문자열을 연결합니다.

Main 메서드에 다음 코드를 추가합니다.

// Specify the 'x-ms-date' header as the current UTC timestamp according to the RFC1123 standard
var date = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture);
// Get the host name corresponding with the 'host' header.
var host = requestUri.Authority;
// Compute a content hash for the 'x-ms-content-sha256' header.
var contentHash = ComputeContentHash(serializedBody);

// Prepare a string to sign.
var stringToSign = $"POST\n{requestUri.PathAndQuery}\n{date};{host};{contentHash}";
// Compute the signature.
var signature = ComputeSignature(stringToSign);
// Concatenate the string, which will be used in the authorization header.
var authorizationHeader = $"HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature={signature}";

requestMessage에 헤더 추가

다음 코드를 사용하여 requestMessage에 필요한 헤더를 추가합니다.

// Add a date header.
requestMessage.Headers.Add("x-ms-date", date);

// Add a host header.
// In C#, the 'host' header is added automatically by the 'HttpClient'. However, this step may be required on other platforms such as Node.js.

// Add a content hash header.
requestMessage.Headers.Add("x-ms-content-sha256", contentHash);

// Add an authorization header.
requestMessage.Headers.Add("Authorization", authorizationHeader);

클라이언트의 테스트

HttpClient를 사용하여 엔드포인트를 호출하고 응답을 확인합니다.

HttpClient httpClient = new HttpClient
{
    BaseAddress = requestUri
};
var response = await httpClient.SendAsync(requestMessage);
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);

필수 조건

시작하기 전에 다음을 확인해야 합니다.

  • 활성 구독이 있는 Azure 계정을 만듭니다. 자세한 내용은 체험 계정 만들기를 참조하세요.
  • Python 다운로드하고 설치합니다.
  • Python을 지원하는 Visual Studio Code 또는 기타 IDE를 다운로드하고 설치합니다.
  • Azure Communication Services 리소스를 만듭니다. 자세한 내용은 Azure Communication Services 리소스 만들기를 참조하세요. 이 자습서에는 resource_endpoint_nameresource_endpoint_secret이 필요합니다.

Python을 사용하여 HTTP 요청 서명

액세스 키 인증은 공유 비밀 키를 사용하여 각 HTTP 요청에 대해 HMAC 서명을 생성합니다. 이 서명은 SHA256 알고리즘을 사용하여 생성되며 HMAC-SHA256 체계를 사용하여 Authorization 헤더에 전송됩니다. 예시:

Authorization: "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=<hmac-sha256-signature>"

hmac-sha256-signature은 다음으로 구성됩니다.

  • HTTP 동사(예: GET 또는 PUT )
  • HTTP 요청 경로
  • x-ms-date
  • Host
  • x-ms-content-sha256

설정

다음 단계에서는 인증 헤더를 생성하는 방법을 설명합니다.

새 Python 스크립트 만들기

선택한 Visual Studio Code 또는 기타 IDE/편집기를 열고 sign_hmac_tutorial.py라는 새 파일을 만듭니다. 이 파일을 알려진 폴더에 저장합니다.

필요한 가져오기 추가

sign_hmac_tutorial.py 스크립트를 다음 코드로 업데이트하여 시작합니다.

import base64
import hashlib
import hmac
import json
from datetime import datetime, timezone
from urllib import request

요청에 대한 데이터 준비

이 예제에서는 Communication Services 인증 API (버전 2021-03-07)를 사용하여 새 ID를 만들도록 요청에 서명합니다.

sign_hmac_tutorial.py 스크립트에 다음 코드를 추가합니다.

  • resource_endpoint_name을 실제 리소스 엔드포인트 이름 값으로 바꿉니다. 이 값은 Azure Communication Services 리소스의 개요 섹션에서 찾을 수 있습니다. 이 값은 “https://” 뒤에 있는 “엔드포인트” 값입니다.
  • resource_endpoint_secret을 실제 리소스 엔드포인트 비밀 값으로 바꿉니다. 이 값은 Azure Communication Services 리소스의 키 섹션에서 찾을 수 있습니다. 이 값은 “키” 값(기본 또는 보조)입니다.
host = "resource_endpoint_name"
resource_endpoint = f"https://{host}"
path_and_query = "/identities?api-version=2021-03-07"
secret = "resource_endpoint_secret"

# Create a uri you are going to call.
request_uri = f"{resource_endpoint}{path_and_query}"

# Endpoint identities?api-version=2021-03-07 accepts list of scopes as a body.
body = { "createTokenWithScopes": ["chat"] }

serialized_body = json.dumps(body)
content = serialized_body.encode("utf-8")

콘텐츠 해시 만들기

콘텐츠 해시는 HMAC 서명의 일부입니다. 다음 코드를 추가하여 콘텐츠 해시를 컴퓨팅합니다. sign_hmac_tutorial.py 스크립트에 이 메서드를 추가할 수 있습니다.

def compute_content_hash(content):
    sha_256 = hashlib.sha256()
    sha_256.update(content)
    hashed_bytes = sha_256.digest()
    base64_encoded_bytes = base64.b64encode(hashed_bytes)
    content_hash = base64_encoded_bytes.decode('utf-8')
    return content_hash

서명 컴퓨팅

다음 코드를 사용하여 HMAC 서명을 계산하는 메서드를 만듭니다.

def compute_signature(string_to_sign, secret):
    decoded_secret = base64.b64decode(secret)
    encoded_string_to_sign = string_to_sign.encode('utf-8')
    hashed_bytes = hmac.digest(decoded_secret, encoded_string_to_sign, digest=hashlib.sha256)
    encoded_signature = base64.b64encode(hashed_bytes)
    signature = encoded_signature.decode('utf-8')
    return signature

RFC1123 표준에 따라 현재 UTC 타임스탬프 가져오기

다음 코드를 사용하여 로캘 설정과 관계없이 원하는 날짜 형식을 가져옵니다.

def format_date(dt):
    days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
    utc = dt.utctimetuple()

    return "{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT".format(
    days[utc.tm_wday],
    utc.tm_mday,
    months[utc.tm_mon-1],
    utc.tm_year,
    utc.tm_hour, 
    utc.tm_min, 
    utc.tm_sec)

권한 부여 헤더 문자열 만들기

이제 권한 부여 헤더에 추가할 문자열을 구성합니다.

  1. 서명할 헤더의 값을 준비합니다.
    1. 협정 세계시(UTC) 시간대를 사용하여 현재 타임스탬프를 지정합니다.
    2. 요청 기관(DNS 호스트 이름 또는 IP 주소 및 포트 번호)을 가져옵니다.
    3. 콘텐츠 해시를 계산합니다.
  2. 서명할 문자열을 준비합니다.
  3. 서명을 계산합니다.
  4. 권한 부여 헤더에 사용되는 문자열을 연결합니다.

sign_hmac_tutorial.py 스크립트에 다음 코드를 추가합니다.

# Specify the 'x-ms-date' header as the current UTC timestamp according to the RFC1123 standard
utc_now = datetime.now(timezone.utc)
date = format_date(utc_now)
# Compute a content hash for the 'x-ms-content-sha256' header.
content_hash = compute_content_hash(content)

# Prepare a string to sign.
string_to_sign = f"POST\n{path_and_query}\n{date};{host};{content_hash}"
# Compute the signature.
signature = compute_signature(string_to_sign, secret)
# Concatenate the string, which will be used in the authorization header.
authorization_header = f"HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature={signature}"

헤더 추가

다음 코드를 사용하여 필요한 헤더를 추가합니다.

request_headers = {}

# Add a date header.
request_headers["x-ms-date"] = date

# Add content hash header.
request_headers["x-ms-content-sha256"] = content_hash

# Add authorization header.
request_headers["Authorization"] = authorization_header

# Add content type header.
request_headers["Content-Type"] = "application/json"

클라이언트의 테스트

엔드포인트를 호출하고 응답을 확인합니다.

req = request.Request(request_uri, content, request_headers, method='POST')
with request.urlopen(req) as response:
  response_string = json.load(response)
print(response_string)

리소스 정리

Communication Services 구독을 정리하고 제거하려면 리소스 또는 리소스 그룹을 삭제합니다. 리소스 그룹을 삭제하면 해당 리소스 그룹에 연결된 다른 모든 리소스가 함께 삭제됩니다. Azure Communication Services 리소스 정리Azure Functions 리소스 정리에 대해 자세히 알아볼 수 있습니다.

다음 단계

다음을 수행할 수도 있습니다.