다음을 통해 공유


HMAC(해시 기반 메시지 인증 코드)를 사용하여 HTTP 요청에 서명

이 문서에서는 HMAC(해시 기반 메시지 인증 코드) 서명을 사용하여 HTTP 요청에 서명하는 방법을 설명합니다.

참고

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

이 자습서에서는 다음 방법을 알아봅니다.

  • 요청 메시지를 만듭니다.
  • 콘텐츠 해시를 만듭니다.
  • 서명을 계산합니다.
  • 인증 헤더 문자열을 만듭니다.
  • 헤더를 추가합니다.

필수 구성 요소

  • 활성 구독이 있는 Azure 계정을 만듭니다. Azure 구독이 없으면 무료 계정 만들기를 참조하세요.
  • Visual Studio를 설치합니다.
  • Azure Communication Services 리소스를 만듭니다. 리소스가 없으면 Communication Services 리소스 만들기를 참조하세요. 이 자습서에서는 resourceEndpointresourceAccessKey 매개 변수를 기록해야 합니다.

C#에 HTTP 요청 서명

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

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-날짜
  • 호스트
  • x-ms-콘텐츠-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 인증 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 서명의 일부입니다. 다음 코드를 추가하여 콘텐츠 해시를 컴퓨팅합니다. 이 메서드를 Program.cs 메서드 아래 Main에 추가할 수 있습니다.

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(Domain Name System) 호스트 이름이나 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 계정을 만듭니다. Azure 구독이 없으면 무료 계정 만들기를 참조하세요.
  • Python 다운로드하고 설치합니다.
  • Python을 지원하는 Visual Studio Code 또는 다른 IDE(통합 개발 환경)를 다운로드하여 설치합니다.
  • Azure Communication Services 리소스를 만듭니다. 리소스가 없으면 Communication Services 리소스 만들기를 참조하세요. 이 예에서는 resource_endpoint_nameresource_endpoint_secret 매개 변수가 필요합니다.

Python을 사용하여 HTTP 요청 서명

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

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-날짜
  • 호스트
  • x-ms-콘텐츠-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을 실제 리소스 엔드포인트 이름 값으로 바꿉니다. 이 값은 Communication Services 리소스의 개요 섹션에서 찾을 수 있습니다. Endpoint 후의 https://의 값입니다.
  • resource_endpoint_secret을 실제 리소스 엔드포인트 비밀 값으로 바꿉니다. 이 값은 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 the 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(Domain Name System) 호스트 이름이나 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 a content hash header.
request_headers["x-ms-content-sha256"] = content_hash

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

# Add a 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 리소스를 정리하는 방법에 대해 자세히 알아볼 수 있습니다.