Firmare una richiesta HTTP

In questa esercitazione si apprenderà come firmare una richiesta HTTP con una firma HMAC.

Nota

Si consiglia vivamente di usare Gli SDK di Azure. L'approccio descritto di seguito è un'opzione di fallback per i casi in cui gli SDK di Azure non possono essere usati per qualsiasi motivo.

Prerequisiti

Prima di iniziare, assicurarsi di:

Firmare una richiesta HTTP con C #

L'autenticazione con 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>"

L'oggetto hmac-sha256-signature è costituito da:

  • Verbo HTTP (ad esempio, GET o PUT)
  • Percorso della richiesta HTTP
  • x-ms-date
  • Host
  • x-ms-content-sha256

Installazione

I passaggi seguenti descrivono come costruire l'intestazione di autorizzazione.

Creare una nuova applicazione C#

In una finestra della console, ad esempio cmd, PowerShell o Bash, usare il dotnet new comando 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. Usare il comando per compilare l'applicazione dotnet build .

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 firmerà 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 dell'endpoint risorsa reale.

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 a Progam.cs nel Main metodo .

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 di autorizzazione

Verrà ora creata la stringa che verrà aggiunta all'intestazione di 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'autorità di richiesta (nome host DNS o indirizzo IP e numero di porta).
    3. Calcolare un hash del contenuto.
  2. Preparare una stringa per firmare.
  3. Calcolare la firma.
  4. Concatenare la stringa, che verrà 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 a 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

Prima di iniziare, assicurarsi di:

Firmare una richiesta HTTP con Python

L'autenticazione con 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>"

L'oggetto hmac-sha256-signature è costituito da:

  • Verbo HTTP (ad esempio, GET o PUT)
  • Percorso della richiesta HTTP
  • x-ms-date
  • Host
  • x-ms-content-sha256

Eseguire la configurazione

I passaggi seguenti descrivono come costruire l'intestazione di autorizzazione.

Creare un nuovo script Python

Aprire Visual Studio Code o un altro IDE o un altro editor a scelta e creare un nuovo 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 firmerà 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 valore del nome dell'endpoint della risorsa reale. Questo valore è disponibile nella sezione Panoramica della risorsa Servizi di comunicazione di Azure. È il valore di "Endpoint" dopo "https://".
  • Sostituire resource_endpoint_secret con il valore del segreto dell'endpoint della risorsa reale. Questo valore è disponibile nella sezione Chiavi della risorsa Servizi di comunicazione di Azure. È il valore di "Key" ( 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 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 il timestamp UTC corrente in base allo standard RFC1123

Usare il codice seguente per ottenere il formato di 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 di autorizzazione

Verrà ora creata la stringa che verrà aggiunta all'intestazione di 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'autorità di richiesta (nome host DNS o indirizzo IP e numero di porta).
    3. Calcolare un hash del contenuto.
  2. Preparare una stringa per firmare.
  3. Calcolare la firma.
  4. Concatenare la stringa, che verrà 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 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"

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. Eliminando il gruppo di risorse vengono eliminate anche tutte le altre risorse associate. Altre informazioni sulla pulizia delle risorse Servizi di comunicazione di Azure e sulla pulizia delle risorse Funzioni di Azure.

Passaggi successivi

È anche possibile: