HTTP 要求に署名する

このチュートリアルでは、HMAC 署名を使用して HTTP 要求に署名する方法について学習します。

Note

Azure SDK の使用を強くお勧めします。 ここで説明するアプローチは、何らかの理由で Azure SDK を使用できない場合のフォールバック オプションです。

前提条件

開始する前に、必ず次のことを行ってください。

  • アクティブなサブスクリプションがある Azure アカウントを作成します。 詳細については、アカウントの無料作成に関するページを参照してください。
  • Visual Studio のインストール。
  • Azure Communication Services リソースを作成します。 詳細については、Azure Communication Services リソースの作成に関するページを参照してください。 このチュートリアル用に、自分の resourceEndpointresourceAccessKey を記録する必要があります。

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

セットアップ

以下の手順で、Authorization ヘッダーを作成する方法を示します。

新しい C# アプリケーションを作成する

cmd、PowerShell、Bash などのコンソール ウィンドウで dotnet new コマンドを使用して、新しいコンソール アプリを SignHmacTutorial という名前で作成します。 このコマンドにより、1 つのソース ファイルを使用する単純な "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.
        }
    }
}

要求メッセージを作成する

この例では、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 署名の一部です。 コンテンツ ハッシュを計算するには、次のコードを使用します。 このメソッドは、Progam.csMain メソッドの下に追加できます。

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 リソースのアクセス キーに置き換えてください。

Authorization ヘッダー文字列を作成する

次に、Authorization ヘッダーに追加する文字列を作成します。

  1. 署名するヘッダーの値を準備します。
    1. 現在のタイムスタンプを協定世界時 (UTC) のタイムゾーンで指定します。
    2. 要求機関 (DNS ホスト名または IP アドレスとポート番号) を取得します。
    3. コンテンツ ハッシュを計算します。
  2. 署名する文字列を準備します。
  3. 署名を計算します。
  4. Authorization ヘッダーで使用される文字列を連結します。

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 をダウンロードしてインストールします。
  • Visual Studio Code、または Python をサポートするその他の 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

セットアップ

以下の手順で、Authorization ヘッダーを作成する方法を示します。

新しい 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 Authentication API (バージョン 2021-03-07) を使用して新しい ID を作成するために、要求に署名します

次のコードを sign_hmac_tutorial.py スクリプトに追加します。

  • resource_endpoint_name を実際のリソース エンドポイントの名前の値に置き換えます。 この値は、Azure Communication Services リソースの [概要] セクションに表示されます。 これは、"Endpoint" の値であり、"https://" の後に表示されます。
  • resource_endpoint_secret を実際のリソース エンドポイントのシークレット値に置き換えます。 この値は、Azure Communication Services リソースの [キー] セクションに表示されます。 これは、"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 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)

Authorization ヘッダー文字列を作成する

次に、Authorization ヘッダーに追加する文字列を作成します。

  1. 署名するヘッダーの値を準備します。
    1. 現在のタイムスタンプを協定世界時 (UTC) のタイムゾーンで指定します。
    2. 要求機関 (DNS ホスト名または IP アドレスとポート番号) を取得します。
    3. コンテンツ ハッシュを計算します。
  2. 署名する文字列を準備します。
  3. 署名を計算します。
  4. Authorization ヘッダーで使用される文字列を連結します。

次のコードを 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 のリソースのクリーンアップに関する詳細を確認できます。

次の手順

次の記事もご覧ください。