Generare token

Questa guida spiega come generare un token e i campi obbligatori e facoltativi per i token.

Per creare un token, devi comporre una stringa da firmare, indicata come valore firmato in questa guida. Il valore firmato include parametri che descrivono i contenuti che stai proteggendo, la data e l'ora di scadenza del valore firmato e così via.

Utilizza il valore firmato durante la creazione di una stringa di token. Puoi creare una stringa di token componendo i parametri per il token, ad esempio un codice HMAC (Hash-based Message Authentication Code) con chiave simmetrica del valore firmato.

Media CDN utilizza il token composto finale per proteggere i tuoi contenuti.

Crea un token

  1. Crea un valore firmato concatenando una stringa contenente i campi obbligatori del token e i campi facoltativi del token che ti interessano. Separa ogni campo e tutti i parametri con un carattere tilde ~.

  2. Firma il valore firmato con una firma Ed25519 o un HMAC con chiave simmetrica.

  3. Componi il token concatenando una stringa contenente i campi obbligatori e facoltativi del token. Separa ogni campo e eventuali parametri con il carattere tilde ~.

    Quando componi il token, i valori di ciascun parametro sono uguali tra il valore firmato e la stringa del token, con le seguenti eccezioni:

    • FullPath
    • Headers

Il seguente esempio di codice mostra come creare un token in modo programmatico:

Python

Per autenticarti a Media CDN, configura le Credenziali predefinite dell'applicazione. Per maggiori informazioni, consulta Configurare l'autenticazione per un ambiente di sviluppo locale.

import base64
import datetime
import hashlib
import hmac

import cryptography.hazmat.primitives.asymmetric.ed25519 as ed25519


def base64_encoder(value: bytes) -> str:
    """
    Returns a base64-encoded string compatible with Media CDN.

    Media CDN uses URL-safe base64 encoding and strips off the padding at the
    end.
    """
    encoded_bytes = base64.urlsafe_b64encode(value)
    encoded_str = encoded_bytes.decode("utf-8")
    return encoded_str.rstrip("=")


def sign_token(
    base64_key: bytes,
    signature_algorithm: str,
    start_time: datetime.datetime = None,
    expiration_time: datetime.datetime = None,
    url_prefix: str = None,
    full_path: str = None,
    path_globs: str = None,
    session_id: str = None,
    data: str = None,
    headers: str = None,
    ip_ranges: str = None,
) -> str:
    """Gets the Signed URL Suffix string for the Media CDN' Short token URL requests.
    One of (`url_prefix`, `full_path`, `path_globs`) must be included in each input.
    Args:
        base64_key: Secret key as a base64 encoded string.
        signature_algorithm: Algorithm can be either `SHA1` or `SHA256` or `Ed25519`.
        start_time: Start time as a UTC datetime object.
        expiration_time: Expiration time as a UTC datetime object. If None, an expiration time 1 hour from now will be used.
        url_prefix: the URL prefix to sign, including protocol.
                    For example: http://example.com/path/ for URLs under /path or http://example.com/path?param=1
        full_path:  A full path to sign, starting with the first '/'.
                    For example: /path/to/content.mp4
        path_globs: a set of ','- or '!'-delimited path glob strings.
                    For example: /tv/*!/film/* to sign paths starting with /tv/ or /film/ in any URL.
        session_id: a unique identifier for the session
        data: data payload to include in the token
        headers: header name and value to include in the signed token in name=value format.  May be specified more than once.
                    For example: [{'name': 'foo', 'value': 'bar'}, {'name': 'baz', 'value': 'qux'}]
        ip_ranges: A list of comma separated ip ranges. Both IPv4 and IPv6 ranges are acceptable.
                    For example: "203.0.113.0/24,2001:db8:4a7f:a732/64"

    Returns:
        The Signed URL appended with the query parameters based on the
        specified URL prefix and configuration.
    """

    decoded_key = base64.urlsafe_b64decode(base64_key)
    algo = signature_algorithm.lower()

    # For most fields, the value we put in the token and the value we must sign
    # are the same.  The FullPath and Headers use a different string for the
    # value to be signed compared to the token.  To illustrate this difference,
    # we'll keep the token and the value to be signed separate.
    tokens = []
    to_sign = []

    # check for `full_path` or `path_globs` or `url_prefix`
    if full_path:
        tokens.append("FullPath")
        to_sign.append(f"FullPath={full_path}")
    elif path_globs:
        path_globs = path_globs.strip()
        field = f"PathGlobs={path_globs}"
        tokens.append(field)
        to_sign.append(field)
    elif url_prefix:
        field = "URLPrefix=" + base64_encoder(url_prefix.encode("utf-8"))
        tokens.append(field)
        to_sign.append(field)
    else:
        raise ValueError(
            "User Input Missing: One of `url_prefix`, `full_path` or `path_globs` must be specified"
        )

    # check & parse optional params
    if start_time:
        epoch_duration = start_time.astimezone(
            tz=datetime.timezone.utc
        ) - datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc)
        field = f"Starts={int(epoch_duration.total_seconds())}"
        tokens.append(field)
        to_sign.append(field)

    if not expiration_time:
        expiration_time = datetime.datetime.now() + datetime.timedelta(hours=1)
        epoch_duration = expiration_time.astimezone(
            tz=datetime.timezone.utc
        ) - datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc)
    else:
        epoch_duration = expiration_time.astimezone(
            tz=datetime.timezone.utc
        ) - datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc)
    field = f"Expires={int(epoch_duration.total_seconds())}"
    tokens.append(field)
    to_sign.append(field)

    if session_id:
        field = f"SessionID={session_id}"
        tokens.append(field)
        to_sign.append(field)

    if data:
        field = f"Data={data}"
        tokens.append(field)
        to_sign.append(field)

    if headers:
        header_names = []
        header_pairs = []
        for each in headers:
            header_names.append(each["name"])
            header_pairs.append("%s=%s" % (each["name"], each["value"]))
        tokens.append(f"Headers={','.join(header_names)}")
        to_sign.append(f"Headers={','.join(header_pairs)}")

    if ip_ranges:
        field = f"IPRanges={base64_encoder(ip_ranges.encode('ascii'))}"
        tokens.append(field)
        to_sign.append(field)

    # generating token
    to_sign = "~".join(to_sign)
    to_sign_bytes = to_sign.encode("utf-8")
    if algo == "ed25519":
        digest = ed25519.Ed25519PrivateKey.from_private_bytes(decoded_key).sign(
            to_sign_bytes
        )
        tokens.append("Signature=" + base64_encoder(digest))
    elif algo == "sha256":
        signature = hmac.new(
            decoded_key, to_sign_bytes, digestmod=hashlib.sha256
        ).hexdigest()
        tokens.append("hmac=" + signature)
    elif algo == "sha1":
        signature = hmac.new(
            decoded_key, to_sign_bytes, digestmod=hashlib.sha1
        ).hexdigest()
        tokens.append("hmac=" + signature)
    else:
        raise ValueError(
            "Input Missing Error: `signature_algorithm` can only be one of `sha1`, `sha256` or `ed25519`"
        )
    return "~".join(tokens)

Java

Per autenticarti a Media CDN, configura le Credenziali predefinite dell'applicazione. Per maggiori informazioni, consulta Configurare l'autenticazione per un ambiente di sviluppo locale.


import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.signers.Ed25519Signer;
import org.bouncycastle.util.encoders.Hex;

public class DualToken {

  public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
    // TODO(developer): Replace these variables before running the sample.
    // Secret key as a base64 encoded string.
    byte[] base64Key = new byte[]{};
    // Algorithm can be one of these: SHA1, SHA256, or Ed25519.
    String signatureAlgorithm = "ed25519";
    // (Optional) Start time as a UTC datetime object.
    DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
    Optional<Instant> startTime = Optional.empty();
    // Expiration time as a UTC datetime object.
    // If None, an expiration time that's an hour after the current time is used.
    Instant expiresTime = Instant.from(formatter.parse("2022-09-13T12:00:00Z"));

    // ONE OF (`urlPrefix`, `fullPath`, `pathGlobs`) must be included in each input.
    // The URL prefix and protocol to sign.
    // For example: http://example.com/path/ for URLs under /path or http://example.com/path?param=1
    Optional<String> urlPrefix = Optional.empty();
    // A full path to sign, starting with the first '/'.
    // For example: /path/to/content.mp4
    Optional<String> fullPath = Optional.of("http://10.20.30.40/");
    // A set of path glob strings delimited by ',' or '!'.
    // For example: /tv/*!/film/* to sign paths starting with /tv/ or /film/ in any URL.
    Optional<String> pathGlobs = Optional.empty();

    // (Optional) A unique identifier for the session.
    Optional<String> sessionId = Optional.empty();
    // (Optional) Data payload to include in the token.
    Optional<String> data = Optional.empty();
    // (Optional) Header name and value to include in the signed token in name=value format.
    // May be specified more than once.
    // For example: [{'name': 'foo', 'value': 'bar'}, {'name': 'baz', 'value': 'qux'}]
    Optional<List<Header>> headers = Optional.empty();
    // (Optional) A list of comma-separated IP ranges. Both IPv4 and IPv6 ranges are acceptable.
    // For example: "203.0.113.0/24,2001:db8:4a7f:a732/64"
    Optional<String> ipRanges = Optional.empty();

    DualToken.signToken(
        base64Key,
        signatureAlgorithm,
        startTime,
        expiresTime,
        urlPrefix,
        fullPath,
        pathGlobs,
        sessionId,
        data,
        headers,
        ipRanges);
  }

  // Gets the signed URL suffix string for the Media CDN short token URL requests.
  // Result:
  //     The signed URL appended with the query parameters based on the
  // specified URL prefix and configuration.
  public static void signToken(
      byte[] base64Key, String signatureAlgorithm, Optional<Instant> startTime,
      Instant expirationTime, Optional<String> urlPrefix, Optional<String> fullPath,
      Optional<String> pathGlobs, Optional<String> sessionId, Optional<String> data,
      Optional<List<Header>> headers, Optional<String> ipRanges)
      throws NoSuchAlgorithmException, InvalidKeyException {

    String field = "";
    byte[] decodedKey = Base64.getUrlDecoder().decode(base64Key);

    // For most fields, the value in the token and the value to sign
    // are the same. Compared to the token, the FullPath and Headers
    // use a different string for the value to sign. To illustrate this difference,
    // we'll keep the token and the value to be signed separate.
    List<String> tokens = new ArrayList<>();
    List<String> toSign = new ArrayList<>();

    // Check for `fullPath` or `pathGlobs` or `urlPrefix`.
    if (fullPath.isPresent()) {
      tokens.add("FullPath");
      toSign.add(String.format("FullPath=%s", fullPath.get()));
    } else if (pathGlobs.isPresent()) {
      field = String.format("PathGlobs=%s", pathGlobs.get().trim());
      tokens.add(field);
      toSign.add(field);
    } else if (urlPrefix.isPresent()) {
      field = String.format("URLPrefix=%s",
          base64Encoder(urlPrefix.get().getBytes(StandardCharsets.UTF_8)));
      tokens.add(field);
      toSign.add(field);
    } else {
      throw new IllegalArgumentException(
          "User Input Missing: One of `urlPrefix`, `fullPath` or `pathGlobs` must be specified");
    }

    // Check & parse optional params.
    long epochDuration;
    if (startTime.isPresent()) {
      epochDuration = ChronoUnit.SECONDS.between(Instant.EPOCH, startTime.get());
      field = String.format("Starts=%s", epochDuration);
      tokens.add(field);
      toSign.add(field);
    }

    if (expirationTime == null) {
      expirationTime = Instant.now().plus(1, ChronoUnit.HOURS);
    }
    epochDuration = ChronoUnit.SECONDS.between(Instant.EPOCH, expirationTime);
    field = String.format("Expires=%s", epochDuration);
    tokens.add(field);
    toSign.add(field);

    if (sessionId.isPresent()) {
      field = String.format("SessionID=%s", sessionId.get());
      tokens.add(field);
      toSign.add(field);
    }

    if (data.isPresent()) {
      field = String.format("Data=%s", data.get());
      tokens.add(field);
      toSign.add(field);
    }

    if (headers.isPresent()) {
      List<String> headerNames = new ArrayList<>();
      List<String> headerPairs = new ArrayList<>();

      for (Header entry : headers.get()) {
        headerNames.add(entry.getName());
        headerPairs.add(String.format("%s=%s", entry.getName(), entry.getValue()));
      }
      tokens.add(String.format("Headers=%s", String.join(",", headerNames)));
      toSign.add(String.format("Headers=%s", String.join(",", headerPairs)));
    }

    if (ipRanges.isPresent()) {
      field = String.format("IPRanges=%s",
          base64Encoder(ipRanges.get().getBytes(StandardCharsets.US_ASCII)));
      tokens.add(field);
      toSign.add(field);
    }

    // Generate token.
    String toSignJoined = String.join("~", toSign);
    byte[] toSignBytes = toSignJoined.getBytes(StandardCharsets.UTF_8);
    String algorithm = signatureAlgorithm.toLowerCase();

    if (algorithm.equalsIgnoreCase("ed25519")) {
      Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(decodedKey, 0);
      Ed25519Signer signer = new Ed25519Signer();
      signer.init(true, privateKey);
      signer.update(toSignBytes, 0, toSignBytes.length);
      byte[] signature = signer.generateSignature();
      tokens.add(String.format("Signature=%s", base64Encoder(signature)));
    } else if (algorithm.equalsIgnoreCase("sha256")) {
      String sha256 = "HmacSHA256";
      Mac mac = Mac.getInstance(sha256);
      SecretKeySpec secretKeySpec = new SecretKeySpec(decodedKey, sha256);
      mac.init(secretKeySpec);
      byte[] signature = mac.doFinal(toSignBytes);
      tokens.add(String.format("hmac=%s", Hex.toHexString(signature)));
    } else if (algorithm.equalsIgnoreCase("sha1")) {
      String sha1 = "HmacSHA1";
      Mac mac = Mac.getInstance(sha1);
      SecretKeySpec secretKeySpec = new SecretKeySpec(decodedKey, sha1);
      mac.init(secretKeySpec);
      byte[] signature = mac.doFinal(toSignBytes);
      tokens.add(String.format("hmac=%s", Hex.toHexString(signature)));
    } else {
      throw new Error(
          "Input Missing Error: `signatureAlgorithm` can only be one of `sha1`, `sha256` or "
              + "`ed25519`");
    }
    // The signed URL appended with the query parameters based on the
    // specified URL prefix and configuration.
    System.out.println(String.join("~", tokens));
  }

  // Returns a base64-encoded string compatible with Media CDN.
  // Media CDN uses URL-safe base64 encoding and strips off the padding at the
  // end.
  public static String base64Encoder(byte[] value) {
    byte[] encodedBytes = Base64.getUrlEncoder().withoutPadding().encode(value);
    return new String(encodedBytes, StandardCharsets.UTF_8);
  }

  public static class Header {

    private String name;
    private String value;

    public Header(String name, String value) {
      this.name = name;
      this.value = value;
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    public String getValue() {
      return value;
    }

    public void setValue(String value) {
      this.value = value;
    }

    @Override
    public String toString() {
      return "Header{"
          + "name='" + name + '\''
          + ", value='" + value + '\''
          + '}';
    }
  }

}

Le sezioni seguenti descrivono i campi utilizzati dai token.

Campi token obbligatori

I seguenti campi sono obbligatori per ogni token:

  • Expires
  • Uno dei seguenti:
    • PathGlobs
    • URLPrefix
    • FullPath
  • Uno dei seguenti:
    • Signature
    • hmac

Se non diversamente specificato, i nomi dei parametri e i relativi valori sono sensibili alle maiuscole.

La tabella seguente illustra ciascun parametro:

Nome campo / alias Parametri token Valore firmato

Expires

exp

Secondi interi trascorsi dall'epoca di Unix (1970-01-01T00:00:00Z) Expires=EXPIRATION_TIME, dopodiché il token non è più valido.

PathGlobs

paths, acl

Un elenco di massimo cinque segmenti di percorso a cui concedere l'accesso. I segmenti possono essere delimitati da virgole (,) o da punti esclamativi (!), ma non da entrambi.

PathGlobs supporta i caratteri jolly nei percorsi utilizzando asterischi (*) e punti interrogativi (?). Un singolo carattere asterisco (*) si estende su un numero qualsiasi di segmenti di percorso, diversamente dalla sintassi di corrispondenza dei pattern per pathMatchTemplate.

I parametri di percorso, indicati mediante punti e virgola (;), non sono consentiti perché creano ambiguità durante la corrispondenza.

Per questi motivi, assicurati che l'URL non contenga i seguenti caratteri speciali: ,!*?;

PathGlobs=PATHS
URLPrefix

Un URL con codifica base64 sicuro per il web che include il protocollo http:// o https:// fino a un punto scelto da te.

Ad esempio, alcuni valori validi di URLPrefix per `https://example.com/foo/bar.ts` sono `https://example.com`, `https://example.com/foo` e `https://example.com/foo/bar`.

URLPrefix=BASE_64_URL_PREFIX
FullPath Nessuno. Quando specifichi FullPath in un token, non duplicare il percorso specificato nel valore firmato. In un token, includi il nome del campo senza =. FullPath=FULL_PATH_TO_OBJECT
Signature Una versione della firma con codifica Base64 sicura per il web. Non applicabile
hmac Una versione con codifica Base64 sicura per il web del valore HMAC. Non applicabile

Sintassi dei caratteri jolly PathGlobs

La tabella seguente illustra la sintassi del carattere jolly PathGlobs.

Operatore Corrisponde a Esempi
* (asterisco) Corrisponde a zero o più caratteri nel percorso dell'URL, inclusi i caratteri barra (/).
  • /videos/* corrisponde a qualsiasi percorso che inizia con /videos/.
  • /videos/s*/4k/* corrisponde a /videos/s/4k/ e /videos/s01/4k/main.m3u8.
  • /manifests/*/4k/* corrisponde a /manifests/s01/4k/main.m3u8 e /manifests/s01/e01/4k/main.m3u8. Non corrisponde /manifests/4k/main.m3u8.
? (punto interrogativo) Corrisponde a un singolo carattere nel percorso dell'URL, esclusi i caratteri barra (/). /videos/s?main.m3u8 corrispondenze /videos/s1main.m3u8. Non corrisponde a /videos/s01main.m3u8 o /videos/s/main.m3u8.

I glob devono iniziare con un asterisco (*) o una barra (/) per i percorsi URL.

Poiché * e /* corrispondono a tutti i percorsi degli URL, non consigliamo di utilizzarli nei token firmati. Per la massima protezione, assicurati che i glob corrispondano ai contenuti a cui intendi concedere l'accesso.

Campi token facoltativi

Se non diversamente specificato, i nomi dei parametri e i relativi valori sono sensibili alle maiuscole.

La tabella seguente spiega i nomi dei parametri, eventuali alias e dettagli per i parametri facoltativi:

Nome campo / alias Parametri Valore firmato

Starts

st

Secondi interi dall'epoca Unix (1970-01-01T00:00:00Z) Starts=START_TIME
IPRanges

Un elenco di massimo cinque indirizzi IPv4 e IPv6 in formato CIDR per i quali questo URL è valido in formato base64 sicuro per il web. Ad esempio, per specificare gli intervalli IP "192.6.13.13/32,193.5.64.135/32", specifica IPRanges=MTkyLjYuMTMuMTMvMzIsMTkzLjUuNjQuMTM1LzMy.

Gli intervalli IP potrebbero non essere utili da includere nei token quando i client sono esposti al rischio di migrazioni WAN o nei casi in cui il percorso di rete al frontend dell'applicazione è diverso dal percorso di importazione. Media CDN rifiuta i client con un codice HTTP 403 quando si connettono con un indirizzo IP che non fa parte della richiesta firmata.

Di seguito sono riportati i casi in cui Media CDN potrebbe rifiutare i clienti con un codice HTTP 403:

  • Ambienti dual-stack (IPv4, IPv6)
  • Migrazione della connessione (da Wi-Fi a rete mobile e da rete mobile a Wi-Fi)
  • Reti mobili che utilizzano NAT gateway dell'operatore (CGNAT o CGN)
  • TCP multi-path (MPTCP)

Tutti questi fattori possono contribuire a far sì che un determinato client abbia un indirizzo IP non deterministico durante una sessione di riproduzione di un video. Se l'indirizzo IP del cliente cambia dopo che hai concesso l'accesso e il cliente tenta di scaricare un segmento di video nel proprio buffer di riproduzione, riceve un messaggio HTTP 403 da Media CDN.

IPRanges=BASE_64_IP_RANGES

SessionID

id

Una stringa arbitraria, utile per l'analisi dei log o per il monitoraggio della riproduzione.

Per evitare di creare un token non valido, utilizza stringhe codificate in base64 con codifica percentuale o sicure per il web. I seguenti caratteri non devono essere utilizzati per SessionID, in quanto rendono il token non valido: "~", "&" o " " (spazio).

SessionID=SESSION_ID_VALUE

Data

data, payload

Una stringa arbitraria, utile per l'analisi dei log.

Per evitare di creare un token non valido, utilizza stringhe codificate in base64 con codifica percentuale o sicure per il web. I seguenti caratteri non devono essere utilizzati per Data, in quanto rendono il token non valido: "~", "&" o " " (spazio).

data=DATA_VALUE
Headers Un elenco separato da virgole di nomi di campi di intestazione. I nomi delle intestazioni non fanno distinzione tra maiuscole e minuscole per le ricerche nella richiesta. I nomi delle intestazioni nei valori firmati sono sensibili alle maiuscole. Se un'intestazione non è presente, il valore è la stringa vuota. Se sono presenti più copie di un'intestazione, queste vengono concatenate con virgole. Headers=HEADER_1_NAME=HEADER_1_EXPECTED_VALUE, HEADER_2_NAME=HEADER_2_EXPECTED_VALUE

Esempi

Le sezioni seguenti mostrano esempi di generazione di token.

Esempio di utilizzo di FullPath

Considera l'esempio seguente che utilizza il campo FullPath:

  • Articolo richiesto: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Data e ora di scadenza: 160000000

Il valore firmato è:

Expires=160000000~FullPath=/tv/my-show/s01/e01/playlist.m3u8

Per creare un token, firma il valore firmato con una firma Ed25519 o con un HMAC con chiave simmetrica.

Di seguito sono riportati alcuni esempi di token creati da un valore firmato:

Firma Ed25519

Expires=160000000~FullPath~Signature=SIGNATURE_OF_SIGNED_VALUE

dove SIGNATURE_OF_SIGNED_VALUE è la firma ED25519 del valore firmato creato in precedenza.

HMAC con chiave simmetrica

Expires=160000000~FullPath~hmac=HMAC_OF_SIGNED_VALUE

dove HMAC_OF_SIGNED_VALUE è l'HMAC con chiave simmetrica del valore firmato creato in precedenza.

Negli esempi precedenti, FullPath viene fornito nel token, ma il valore non viene ripetuto dal percorso specificato nel valore firmato. In questo modo, puoi firmare il percorso completo della richiesta senza duplicarla nel token.

Esempio di utilizzo di URLPrefix

Considera l'esempio seguente che utilizza il campo URLPrefix:

  • Articolo richiesto: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Data e ora di scadenza: 160000000

Il valore firmato è:

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4

Nell'esempio precedente, abbiamo sostituito il percorso dell'elemento richiesto,http://example.com/tv/my-show/s01/e01/playlist.m3u8 con il percorso dell'elemento in formato Base64 sicuro per il web,aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4.

Per creare un token, firma il valore firmato con una firma Ed25519 o con un HMAC con chiave simmetrica.

Di seguito sono riportati alcuni esempi di token creati da un valore firmato:

Firma Ed25519

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4~Signature=SIGNATURE_OF_SIGNED_VALUE

dove SIGNATURE_OF_SIGNED_VALUE è la firma ED25519 del valore firmato creato in precedenza.

HMAC con chiave simmetrica

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4~hmac=HMAC_OF_SIGNED_VALUE

dove HMAC_OF_SIGNED_VALUE è l'HMAC con chiave simmetrica del valore firmato creato in precedenza.

Esempio di utilizzo di Headers

Considera l'esempio seguente che utilizza il campo Headers:

  • Articolo richiesto: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Data e ora di scadenza: 160000000
  • Valore PathGlobs: *
  • Intestazioni delle richieste previste:
    • user-agent: browser
    • accept: text/html

Il valore firmato è:

Expires=160000000~PathGlobs=*~Headers=user-agent=browser,accept=text/html

Per creare un token, firma il valore firmato con una firma Ed25519 o con un HMAC con chiave simmetrica.

Di seguito sono riportati alcuni esempi di token creati da un valore firmato:

Firma Ed25519

Expires=160000000~PathGlobs=*~Headers=user-agent,accept~Signature=SIGNATURE_OF_SIGNED_VALUE

dove SIGNATURE_OF_SIGNED_VALUE è la firma ED25519 del valore firmato creato in precedenza.

HMAC con chiave simmetrica

Expires=160000000~PathGlobs=*~Headers=user-agent,accept~hmac=HMAC_OF_SIGNED_VALUE

dove HMAC_OF_SIGNED_VALUE è l'HMAC con chiave simmetrica del valore firmato creato in precedenza.

Negli esempi precedenti, Headers=user-agent,accept viene fornito nel token, ma i valori dell'intestazione previsti non vengono ripetuti dal valore firmato. In questo modo puoi firmare coppie chiave-valore specifiche dell'intestazione della richiesta senza duplicare i valori nel token.