共用方式為


使用哈希式訊息驗證碼簽署 HTTP 要求(HMAC)

本文說明如何使用哈希式訊息驗證碼 (HMAC) 簽章簽署 HTTP 要求。

附註

我們建議使用 Azure SDK 簽署 HTTP 要求。 如果 Azure SDK 因任何原因而無法使用,本文所述的方法是後援選項。

在本教學課程中,您將瞭解如何:

  • 建立要求訊息。
  • 建立內容哈希。
  • 計算簽章。
  • 建立授權標頭字串。
  • 新增標頭。

必要條件

  • 建立具有有效訂閱的 Azure 帳戶。 如果您沒有 Azure 訂用帳戶,請參閱 免費建立帳戶
  • 安裝 Visual Studio
  • 建立 Azure 通訊服務資源。 如果您沒有資源,請參閱 建立通訊服務資源。 您需要針對此教學課程記錄 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 動詞指令 (例如 GETPUT)
  • HTTP 要求路徑
  • x-ms-date
  • Host
  • x-ms-content-sha256

設定授權標頭

完成下列步驟以建構授權標頭。

建立新的 C# 應用程式

在主控台視窗中 (例如 cmd、PowerShell 或 Bash),使用 dotnet new 命令建立名為 SignHmacTutorial 的新主控台應用程式。 此命令會建立簡單的 "Hello World" C# 專案,內含單一原始程式檔:Program.cs

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

建立要求訊息

在此範例中,您會使用通訊服務驗證 API(版本 2021-03-07)簽署建立新身分識別的要求。

將下列程式碼新增至 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 方法底下的 Program.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 替換為您實際通訊服務資源的存取鍵。

建立授權標頭字串

現在,您會建構新增至授權標頭的字串。

  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 帳戶。 如果您沒有 Azure 訂用帳戶,請參閱 免費建立帳戶
  • 下載並安裝 Python
  • 下載並安裝支援 Python 的 Visual Studio Code 或其他整合開發環境 (IDE)。
  • 建立 Azure 通訊服務資源。 如果您沒有資源,請參閱 建立通訊服務資源。 您需要您的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 動詞指令 (例如 GETPUT)
  • 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

準備要求的資料

在此範例中,您會使用通訊服務驗證 API (版本 2021-03-07簽署建立新身分識別的要求。

將下列程式碼新增至 sign_hmac_tutorial.py 指令碼。

  • resource_endpoint_name 取代為您的實際資源端點名稱值。 您可以在通訊服務資源的 [概 ] 區段中找到此值。 這是 https:// 之後 Endpoint 的值。
  • resource_endpoint_secret 取代為您的實際資源端點祕密值。 您可以在通訊服務資源的 [ 金鑰 ] 區段中找到此值。 它是 Key 的值,也就是主要或次要。
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)主機名稱或 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)

清除資源

若要清除和移除通訊服務訂閱,請刪除資源或資源群組。 刪除資源群組也會刪除與其相關聯的任何其他資源。 您可以深入瞭解如何 清除 Azure 通訊服務資源清除 Azure Functions 資源