Поделиться через


Подписать HTTP-запрос с помощью хэш-кода аутентификации сообщения (HMAC)

В этой статье описывается, как подписать HTTP-запрос с помощью хэш-кода проверки подлинности сообщений (HMAC).

Note

Мы рекомендуем использовать пакеты SDK Azure для подписывания HTTP-запроса. Подход, описанный в этой статье, является резервным вариантом, если пакеты SDK Azure не могут использоваться по какой-либо причине.

В этом руководстве описано, как:

  • Создайте сообщение запроса.
  • Создайте хэш содержимого.
  • Вычисление подписи.
  • Создайте строку заголовка авторизации.
  • Добавьте заголовки.

Prerequisites

Подписывание HTTP-запроса с помощью C#

Проверка подлинности на основе ключа доступа использует для создания подписей HMAC для всех HTTP-запросов общий секретный ключ. Эта сигнатура создается с использованием алгоритма 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-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. Используйте следующий код, чтобы вычислить хэш содержимого. Этот метод можно добавить в 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 ключом доступа к реальному ресурсу Служб коммуникации.

Создание строки заголовка авторизации

Теперь вы создаете строку, добавляемую в заголовок авторизации.

  1. Подготовьте значения для подписываемых заголовков.
    1. Укажите текущую метку времени с помощью часового пояса UTC.
    2. Получите полномочия для запроса. Используйте имя узла или IP-адрес системы доменных имен (DNS) и номер порта.
    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);

Prerequisites

Подписать HTTP-запрос с помощью Python

Проверка подлинности на основе ключа доступа использует для создания подписей HMAC для всех HTTP-запросов общий секретный ключ. Эта сигнатура создается с использованием алгоритма 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-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 значением имени конечной точки реального ресурса. Это значение можно найти в разделе "Обзор " ресурса служб коммуникации. Это значение Endpoint после https://.
  • Замените resource_endpoint_secret на фактическое секретное значение конечной точки ресурса. Это значение можно найти в разделе "Ключи " ресурса служб коммуникации. Это значение ключа, которое является первичным или вторичным.
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

Получение текущей метки времени UTC в соответствии со стандартом RFC1123

Используйте следующий код, чтобы получить формат даты, который не зависит от параметров языкового стандарта.

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. Получите полномочия для запроса. Используйте имя узла или IP-адрес системы доменных имен (DNS) и номер порта.
    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.