Generar tokens

En esta guía, se explica cómo generar un token y los campos obligatorios y opcionales para los tokens.

Para crear un token, debes redactar una cadena para firmar, a la que nos referimos como valor firmado en esta guía. El valor firmado incluye parámetros que describen el contenido que proteges, la fecha de vencimiento del valor firmado, etcétera.

Usas el valor firmado mientras creas una cadena de tokens. Para crear una cadena de token, debes componer los parámetros del token, como un código de autenticación de mensajes basado en hash (HMAC) de clave simétrica del valor firmado.

Media CDN usa el token compuesto final para ayudar a proteger el contenido.

Crear un token

  1. Crea un valor firmado mediante la concatenación de una cadena que contenga los campos de token obligatorios y los campos de token opcionales deseados. Separa cada campo y cualquier parámetro con un carácter virgulilla ~.

  2. Firma el valor firmado con una firma Ed25519 o un HMAC de clave simétrica.

  3. Para crear el token, concatena una cadena que contenga los campos de token obligatorios y opcionales. Separa cada campo y cualquier parámetro con un carácter virgulilla ~.

    Cuando se compone el token, los valores de cada uno de los parámetros son los mismos entre el valor firmado y la cadena de token, con las siguientes excepciones:

    • FullPath
    • Headers

En la siguiente muestra de código, se muestra cómo crear un token de manera programática:

Python

Para autenticarte en Media CDN, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

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

Para autenticarte en Media CDN, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.


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 + '\''
          + '}';
    }
  }

}

En las siguientes secciones, se describen los campos que usan los tokens.

Campos de token obligatorios

Los siguientes campos son obligatorios para cada token:

  • Expires
  • Uno de los siguientes:
    • PathGlobs
    • URLPrefix
    • FullPath
  • Una de las siguientes opciones:
    • Signature
    • hmac

A menos que se especifique lo contrario, en los nombres de los parámetros y en sus valores, se distingue entre mayúsculas y minúsculas.

En la siguiente tabla, se explica cada parámetro:

Nombres de campos o alias Parámetros de token Valor con firma

Expires

exp

Es un número entero de segundos que transcurrieron desde la época Unix (1970-01-01T00:00:00Z). Expires=EXPIRATION_TIME, después de lo cual el token ya no es válido.

PathGlobs

paths, acl

Es una lista de hasta cinco segmentos de ruta a los que se les otorgará acceso. Los segmentos se pueden delimitar con comas (,) o signos de exclamación (!), pero no con ambos.

PathGlobs admite comodines en las rutas de acceso mediante el uso de asteriscos (*) y signos de interrogación (?). Un solo carácter de asterisco (*) abarca cualquier cantidad de segmentos de ruta, a diferencia de la sintaxis de coincidencia de patrones para pathMatchTemplate.

No se permiten los parámetros de ruta de acceso, que se indican con punto y coma (;), porque crean ambigüedad durante la coincidencia.

Por estos motivos, asegúrate de que tu URL no contenga los siguientes caracteres especiales: ,!*?;

PathGlobs=PATHS
URLPrefix

Una URL codificada en base64 segura para la Web que incluya el protocolo http:// o https:// hasta un punto que elijas

Por ejemplo, algunos valores válidos de URLPrefix para `https://example.com/foo/bar.ts` son "https://example.com", "https://example.com/foo" y "https://example.com/foo/bar".

URLPrefix=BASE_64_URL_PREFIX
FullPath Ninguno Cuando especifiques FullPath en un token, no dupliques la ruta de acceso que especificaste en el valor firmado. En un token, incluye el nombre del campo sin un =. FullPath=FULL_PATH_TO_OBJECT
Signature Una versión codificada en Base64 segura para la Web de la firma. No aplicable
hmac Una versión codificada en base64 segura para la Web del valor de HMAC. No aplicable

Sintaxis de comodines de PathGlobs

En la siguiente tabla, se explica la sintaxis de comodines PathGlobs.

Operador Coincidencias Ejemplos
* (asterisco) Coincide con cero o más caracteres en la ruta de la URL, incluidas las barras diagonales (/).
  • /videos/* coincide con cualquier ruta que comience con /videos/.
  • /videos/s*/4k/* coincide con /videos/s/4k/ y /videos/s01/4k/main.m3u8.
  • /manifests/*/4k/* coincide con /manifests/s01/4k/main.m3u8 y /manifests/s01/e01/4k/main.m3u8. No coincide con /manifests/4k/main.m3u8.
? (signo de interrogación) Coincide con un solo carácter en la ruta de la URL, sin incluir los caracteres de barra diagonal (/). /videos/s?main.m3u8 coincide con /videos/s1main.m3u8. No coincide con /videos/s01main.m3u8 ni /videos/s/main.m3u8.

Los globs deben comenzar con un asterisco (*) o una barra diagonal (/) para las rutas de URL.

Debido a que * y /* coinciden con todas las rutas de URL, no te recomendamos que las uses en tus tokens firmados. Para obtener la máxima protección, asegúrate de que tus globs coincidan con el contenido al que deseas otorgar acceso.

Campos de token opcionales

A menos que se especifique lo contrario, en los nombres de los parámetros y en sus valores, se distingue entre mayúsculas y minúsculas.

En la siguiente tabla, se explican los nombres de los parámetros, los alias y los detalles de los parámetros opcionales:

Nombres de campos o alias Parámetros Valor con firma

Starts

st

Número entero de segundos desde la época Unix (1970-01-01T00:00:00Z) Starts=START_TIME
IPRanges

Una lista de hasta cinco direcciones IPv4 e IPv6 en formato CIDR para las que esta URL es válida en formato base64 seguro para la Web. Por ejemplo, para especificar los rangos de IP “192.6.13.13/32,193.5.64.135/32”, debes especificar IPRanges=MTkyLjYuMTMuMTMvMzIsMTkzLjUuNjQuMTM1LzMy.

Es posible que los IPRanges no sean útiles para incluir en los tokens cuando los clientes están en riesgo de migraciones de WAN o casos en los que la ruta de red al frontend de tu aplicación es diferente de la ruta de entrega. Media CDN rechaza a los clientes con un código HTTP 403 cuando se conectan con una dirección IP que no forma parte de la solicitud firmada.

Los siguientes son casos que pueden provocar que Media CDN rechace a los clientes con un código HTTP 403:

  • Entornos de doble pila (IPv4, IPv6)
  • Migración de conexión (Wi-Fi a móvil y de móvil a Wi-Fi)
  • Redes móviles que usan NAT de puerta de enlace del proveedor (CGNAT o CGN)
  • TCP de varias rutas (MPTCP)

Todos estos factores pueden contribuir a que un cliente determinado tenga una dirección IP no determinista durante una sesión de reproducción de video. Si la dirección IP del cliente cambia después de que emites el acceso y el cliente intenta descargar un segmento de video en su búfer de reproducción, recibe un HTTP 403 de Media CDN.

IPRanges=BASE_64_IP_RANGES

SessionID

id

Es una cadena arbitraria, útil para el análisis de registros o el seguimiento de reproducción.

Para evitar crear un token no válido, usa cadenas codificadas en Base64 con codificación % o seguras para la Web. No se deben usar los siguientes caracteres para SessionID, ya que hacen que el token no sea válido: "~", "&" o " " (espacio).

SessionID=SESSION_ID_VALUE

Data

data, payload

Es una cadena arbitraria, útil para el análisis de registros.

Para evitar crear un token no válido, usa cadenas codificadas en Base64 con codificación % o seguras para la Web. No se deben usar los siguientes caracteres para Data, ya que hacen que el token no sea válido: "~", "&" o " " (espacio).

data=DATA_VALUE
Headers Es una lista de nombres de campos de encabezado separados por comas. Los nombres de encabezado no distinguen mayúsculas de minúsculas para las búsquedas en la solicitud. Los nombres de encabezado en los valores firmados distinguen mayúsculas de minúsculas. Si falta un encabezado, el valor es la cadena vacía. Si hay varias copias de un encabezado, se concatenan con comas. Headers=HEADER_1_NAME=HEADER_1_EXPECTED_VALUE, HEADER_2_NAME=HEADER_2_EXPECTED_VALUE

Ejemplos

En las siguientes secciones, se muestran ejemplos para generar tokens.

Ejemplo de uso de FullPath

Considera el siguiente ejemplo con el campo FullPath:

  • Elemento solicitado: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Hora de vencimiento: 160000000

El valor firmado es el siguiente:

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

Para crear un token, firma el valor firmado con una firma Ed25519 o un HMAC de clave simétrica.

Los siguientes son ejemplos de tokens creados a partir de un valor firmado:

Firma Ed25519

Expires=160000000~FullPath~Signature=SIGNATURE_OF_SIGNED_VALUE

En el que SIGNATURE_OF_SIGNED_VALUE es la firma ED25519 del valor firmado que se creó anteriormente.

HMAC de clave simétrica

Expires=160000000~FullPath~hmac=HMAC_OF_SIGNED_VALUE

En el que HMAC_OF_SIGNED_VALUE es el HMAC de clave simétrica del valor firmado que se creó anteriormente.

En los ejemplos anteriores, se proporciona FullPath en el token, pero el valor no se repite desde la ruta de acceso especificada en el valor firmado. Esto te permite firmar la ruta completa de la solicitud sin duplicarla en el token.

Ejemplo de uso de URLPrefix

Considera el siguiente ejemplo con el campo URLPrefix:

  • Elemento solicitado: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Hora de vencimiento: 160000000

El valor firmado es el siguiente:

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4

En el ejemplo anterior, reemplazamos la ruta de acceso al elemento solicitado, http://example.com/tv/my-show/s01/e01/playlist.m3u8, por la ruta de acceso al elemento en formato Base64 seguro para la Web, aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4.

Para crear un token, firma el valor firmado con una firma Ed25519 o un HMAC de clave simétrica.

Los siguientes son ejemplos de tokens creados a partir de un valor firmado:

Firma Ed25519

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4~Signature=SIGNATURE_OF_SIGNED_VALUE

En el que SIGNATURE_OF_SIGNED_VALUE es la firma ED25519 del valor firmado que se creó anteriormente.

HMAC de clave simétrica

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4~hmac=HMAC_OF_SIGNED_VALUE

En el que HMAC_OF_SIGNED_VALUE es el HMAC de clave simétrica del valor firmado que se creó anteriormente.

Ejemplo de uso de Headers

Considera el siguiente ejemplo con el campo Headers:

  • Elemento solicitado: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Hora de vencimiento: 160000000
  • Valor de PathGlobs: *
  • Encabezados de solicitud esperados:
    • user-agent: browser
    • accept: text/html

El valor firmado es el siguiente:

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

Para crear un token, firma el valor firmado con una firma Ed25519 o un HMAC de clave simétrica.

Los siguientes son ejemplos de tokens creados a partir de un valor firmado:

Firma Ed25519

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

En el que SIGNATURE_OF_SIGNED_VALUE es la firma ED25519 del valor firmado que se creó anteriormente.

HMAC de clave simétrica

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

En el que HMAC_OF_SIGNED_VALUE es el HMAC de clave simétrica del valor firmado que se creó anteriormente.

En los ejemplos anteriores, se proporciona Headers=user-agent,accept en el token, pero los valores de encabezado esperados no se repiten del valor firmado. Esto te permite firmar pares clave-valor específicos del encabezado de solicitud sin duplicar los valores en el token.