Condividi tramite


Firmare una richiesta HTTP usando il codice HMAC (Hash-Based Message Authentication Code)

Questo articolo descrive come firmare una richiesta HTTP con una firma HMAC (Hash-Based Message Authentication Code).

Nota

È consigliabile usare gli SDK di Azure per firmare una richiesta HTTP. L'approccio descritto in questo articolo è un'opzione di fallback se gli SDK di Azure non possono essere usati per qualsiasi motivo.

In questa esercitazione si apprenderà come:

  • Creare un messaggio di richiesta.
  • Creare un hash del contenuto.
  • Calcolare una firma.
  • Creare una stringa di intestazione dell'autorizzazione.
  • Aggiungere intestazioni.

Prerequisiti

Firmare una richiesta HTTP con C#

L'autenticazione della chiave di accesso usa una chiave privata condivisa per generare una firma HMAC per ogni richiesta HTTP. Questa firma viene generata con l'algoritmo SHA256 e viene inviata nell'intestazione Authorization usando lo HMAC-SHA256 schema . Ad esempio:

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

La hmac-sha256-signature è costituita dagli elementi seguenti:

  • Verbo HTTP (ad esempio, GET o PUT)
  • Percorso della richiesta HTTP
  • x-ms-date
  • Padrone di casa / Ospitante / Conduttore (depending on the intended context)
  • x-ms-content-sha256

Impostare l'intestazione dell'autorizzazione

Completare i passaggi seguenti per creare l'intestazione di autorizzazione.

Creare una nuova applicazione C#

In una finestra di una console, ad esempio cmd, PowerShell o Bash, usare il comando dotnet new per creare una nuova app console con il nome SignHmacTutorial. Questo comando crea un semplice progetto C# "Hello World" con un singolo file di origine: Program.cs.

dotnet new console -o SignHmacTutorial

Spostarsi nella cartella dell'app appena creata. Per compilare l'applicazione, usare il dotnet build comando .

cd SignHmacTutorial
dotnet build

Installare il pacchetto

Installare il pacchetto Newtonsoft.Json usato per la serializzazione del corpo.

dotnet add package Newtonsoft.Json

Aggiornare la dichiarazione del Main metodo per supportare il codice asincrono. Usare il codice seguente per iniziare.

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

Creare un messaggio di richiesta

Per questo esempio, si firma una richiesta per creare una nuova identità usando l'API di autenticazione di Servizi di comunicazione (versione 2021-03-07).

Aggiungere il codice seguente al metodo 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")
};

Sostituire resourceEndpoint con il valore reale dell'endpoint della risorsa.

Creare un hash del contenuto

L'hash del contenuto fa parte della firma HMAC. Usare il codice seguente per calcolare l'hash del contenuto. È possibile aggiungere questo metodo al metodo Program.cs sotto il metodo Main.

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

Calcolare una firma

Usare il codice seguente per creare un metodo per calcolare la firma 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);
}

Sostituire resourceAccessKey con una chiave di accesso della risorsa di Servizi di comunicazione reale.

Creare una stringa di intestazione dell'autorizzazione

Viene ora costruita la stringa da aggiungere all'intestazione dell’autorizzazione.

  1. Preparare i valori per le intestazioni da firmare.
    1. Specificare il timestamp corrente usando il fuso orario UTC (Coordinated Universal Time).
    2. Ottenere l'autorizzazione alla richiesta. Usare il nome host DNS (Domain Name System) o l'indirizzo IP e il numero di porta.
    3. Calcolare un hash del contenuto.
  2. Preparare una stringa per la firma.
  3. Calcolare la firma.
  4. Concatenare la stringa usata nell'intestazione dell'autorizzazione.

Aggiungere il codice seguente al metodo 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}";

Aggiungere intestazioni a requestMessage

Usare il codice seguente per aggiungere le intestazioni necessarie al parametro 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);

Testare il client

Chiamare l'endpoint usando HttpCliente controllare la risposta.

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

Prerequisiti

  • Creare un account Azure con una sottoscrizione attiva. Se non si ha una sottoscrizione di Azure, vedere Creare gratuitamente un account.
  • Scaricare e installare Python.
  • Scaricare e installare Visual Studio Code o un altro ambiente di sviluppo integrato (IDE) che supporta Python.
  • Creare una risorsa di Servizi di comunicazione di Azure. Se non si ha una risorsa, vedere Creare una risorsa di Servizi di comunicazione. Hai bisogno dei tuoi parametri resource_endpoint_name e resource_endpoint_secret per questo esempio.

Firmare una richiesta HTTP con Python

L'autenticazione della chiave di accesso usa una chiave privata condivisa per generare una firma HMAC per ogni richiesta HTTP. Questa firma viene generata con l'algoritmo SHA256 e viene inviata nell'intestazione Authorization usando lo HMAC-SHA256 schema . Ad esempio:

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

La hmac-sha256-signature è costituita dagli elementi seguenti:

  • Verbo HTTP (ad esempio, GET o PUT)
  • Percorso della richiesta HTTP
  • x-ms-date
  • Padrone di casa / Ospitante / Conduttore (depending on the intended context)
  • x-ms-content-sha256

Impostare l'intestazione dell'autorizzazione

Eseguire la procedura seguente per costruire l'intestazione dell'autorizzazione.

Creare un nuovo script Python

Aprire Visual Studio Code o un altro IDE o un altro editor preferito. Creare un file denominato sign_hmac_tutorial.py. Salvare il file in una cartella nota.

Aggiungere le importazioni necessarie

Aggiornare lo sign_hmac_tutorial.py script con il codice seguente per iniziare.

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

Preparare i dati per la richiesta

Per questo esempio, si firma una richiesta per creare una nuova identità usando l'API di autenticazione di Servizi di comunicazione (versione 2021-03-07).

Aggiungere il codice seguente allo sign_hmac_tutorial.py script.

  • Sostituire resource_endpoint_name con il nome effettivo dell'endpoint della risorsa. Questo valore è disponibile nella sezione Panoramica della risorsa servizi di comunicazione. È il valore di Endpoint dopo https://.
  • Sostituire resource_endpoint_secret con il valore segreto dell'endpoint della risorsa reale. È possibile trovare questo valore nella sezione Chiavi della risorsa servizi di comunicazione. È il valore di Key, che è primario o secondario.
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")

Creare un hash del contenuto

L'hash del contenuto fa parte della firma HMAC. Usare il codice seguente per calcolare l'hash del contenuto. È possibile aggiungere questo metodo allo sign_hmac_tutorial.py script.

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

Calcolare una firma

Usare il codice seguente per creare un metodo per calcolare la firma 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

Ottenere un timestamp UTC corrente in base allo standard RFC1123

Usare il codice seguente per ottenere il formato della data desiderato indipendente dalle impostazioni locali.

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)

Creare una stringa di intestazione dell'autorizzazione

Viene ora costruita la stringa da aggiungere all'intestazione dell’autorizzazione.

  1. Preparare i valori per le intestazioni da firmare.
    1. Specificare il timestamp corrente usando il fuso orario UTC (Coordinated Universal Time).
    2. Ottenere l'autorizzazione alla richiesta. Usare il nome host DNS (Domain Name System) o l'indirizzo IP e il numero di porta.
    3. Calcolare un hash del contenuto.
  2. Preparare una stringa per la firma.
  3. Calcolare la firma.
  4. Concatenare la stringa usata nell'intestazione dell'autorizzazione.

Aggiungere il codice seguente allo sign_hmac_tutorial.py script.

# 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}"

Aggiungere le intestazioni

Usare il codice seguente per aggiungere le intestazioni necessarie.

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"

Testare il client

Chiamare l'endpoint e controllare la risposta.

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

Pulire le risorse

Per pulire e rimuovere una sottoscrizione di Servizi di comunicazione, eliminare la risorsa o il gruppo di risorse. L'eliminazione del gruppo di risorse comporta anche l'eliminazione di tutte le altre risorse associate. Altre informazioni su come pulire le risorse di Servizi di comunicazione di Azure e pulire le risorse di Funzioni di Azure.