Gerar tokens

Este guia explica como gerar um token e os campos obrigatórios e opcionais para tokens.

Para criar um token, você compõe uma string a ser assinada, que chamamos de valor assinado neste guia. O valor assinado inclui parâmetros que descrevem o conteúdo que você está protegendo, o tempo de expiração do valor assinado e assim por diante.

Você usa o valor assinado ao criar uma string de token. Você cria uma string de token com a composição dos parâmetros do token, como um código de autenticação de mensagem baseado em hash (HMAC) de chave simétrica do valor assinado.

A Media CDN usa o token composto final para ajudar a proteger seu conteúdo.

Criar um token

  1. Crie um valor assinado concatenando uma string que contém os campos de token obrigatórios e os campos de token opcionais desejados. Separe cada campo e todos os parâmetros com um caractere tilde ~.

  2. Assine o valor assinado com uma assinatura Ed25519 ou um HMAC de chave simétrica.

  3. Componha o token concatenando uma string que contém os campos obrigatórios e opcionais. Separe cada campo e todos os parâmetros com um caractere tilde ~.

    Ao compor o token, os valores de cada um dos parâmetros são os mesmos entre o valor assinado e a string de token, com as seguintes exceções:

    • FullPath
    • Headers

O exemplo de código abaixo mostra como criar um token de maneira programática:

Python

Para autenticar no Media CDN, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento 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 autenticar no Media CDN, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento 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 + '\''
          + '}';
    }
  }

}

As seções a seguir descrevem os campos usados por tokens.

Campos de token obrigatórios

Os campos a seguir são obrigatórios para todos os tokens:

  • Expires
  • Uma das seguintes opções:
    • PathGlobs
    • URLPrefix
    • FullPath
  • Uma das seguintes opções:
    • Signature
    • hmac

A menos que especificado de outra forma, os nomes dos parâmetros e os valores deles diferenciam maiúsculas de minúsculas.

A tabela a seguir explica cada parâmetro:

Nome do campo / aliases Parâmetros de token Valor assinado

Expires

exp

Segundos inteiros decorridos desde a época Unix (1970-01-01T00:00:00Z) Expires=EXPIRATION_TIME, após o qual o token não é mais válido.

PathGlobs

paths, acl

Uma lista de até cinco segmentos de caminho para conceder acesso. Os segmentos podem ser delimitados por vírgulas (,) ou pontos de exclamação (!), mas não por ambos.

PathGlobs oferece suporte a caracteres curinga em caminhos usando asteriscos (*) e pontos de interrogação (?). Um único caractere asterisco (*) abrange qualquer número de segmentos de caminho, diferente da sintaxe de correspondência de padrão para pathMatchTemplate.

Os parâmetros de caminho, indicados com ponto e vírgula (;), não são permitidos porque criam ambiguidade durante a correspondência.

Por esses motivos, verifique se o URL não contém os seguintes caracteres especiais: ,!*?;

PathGlobs=PATHS
URLPrefix

Um URL codificado em base64 seguro para Web, incluindo o protocolo http:// ou https:// até um ponto de sua escolha.

Por exemplo, alguns valores válidos de URLPrefix para `https://example.com/foo/bar.ts` são `https://example.com`, `https://example.com/foo` e `https://example.com/foo/bar`.

URLPrefix=BASE_64_URL_PREFIX
FullPath Nenhuma. Ao especificar FullPath em um token, não duplique o caminho especificado no valor assinado. Em um token, inclua o nome do campo sem um =. FullPath=FULL_PATH_TO_OBJECT
Signature Uma versão da assinatura codificada em base64 e segura para a Web. Não relevante
hmac Uma versão codificada em base64 segura para a Web do valor HMAC. Não relevante

Sintaxe de curinga PathGlobs

A tabela a seguir explica a sintaxe de curinga PathGlobs.

Operador Corresponde a Exemplos
* (asterisco) Corresponde a zero ou mais caracteres no caminho do URL, incluindo caracteres de barra (/).
  • /videos/* corresponde a qualquer caminho que comece com /videos/.
  • /videos/s*/4k/* corresponde a /videos/s/4k/ e /videos/s01/4k/main.m3u8.
  • /manifests/*/4k/* corresponde a /manifests/s01/4k/main.m3u8 e /manifests/s01/e01/4k/main.m3u8. Ele não corresponde a /manifests/4k/main.m3u8.
? (ponto de interrogação) Corresponde a um único caractere no caminho do URL, sem incluir caracteres de barra (/). /videos/s?main.m3u8 corresponde /videos/s1main.m3u8. Ele não corresponde a /videos/s01main.m3u8 ou /videos/s/main.m3u8.

Os globs precisam começar com um asterisco (*) ou uma barra (/) para caminhos de URL.

Como * e /* correspondem a todos os caminhos de URL, não recomendamos usar nenhum deles nos tokens assinados. Para proteção máxima, verifique se os globais correspondem ao conteúdo a que você quer conceder acesso.

Campos de token opcionais

A menos que especificado de outra forma, os nomes dos parâmetros e os valores deles diferenciam maiúsculas de minúsculas.

A tabela a seguir explica os nomes dos parâmetros, os aliases e os detalhes dos parâmetros opcionais:

Nome do campo / aliases Parâmetros Valor assinado

Starts

st

Segundos inteiros desde a época Unix (1970-01-01T00:00:00Z) Starts=START_TIME
IPRanges

Uma lista de até cinco endereços IPv4 e IPv6 no formato CIDR para que esse URL é válido no formato base64 seguro para a Web. Por exemplo, para especificar os intervalos de IP "192.6.13.13/32,193.5.64.135/32", especifique IPRanges=MTkyLjYuMTMuMTMvMzIsMTkzLjUuNjQuMTM1LzMy.

Os intervalos de IP podem não ser úteis para incluir em tokens quando os clientes estão em risco de migrações de WAN ou casos em que o caminho de rede para o front-end do aplicativo é diferente do caminho de entrega. O Media CDN rejeita clientes com um código HTTP 403 quando eles se conectam com um endereço IP que não faz parte da solicitação assinada.

Confira a seguir os casos que podem resultar na rejeição de clientes do Media CDN com um código HTTP 403:

  • Ambientes de pilha dupla (IPv4, IPv6)
  • Migração de conexão (Wi-Fi para celular e celular para Wi-Fi)
  • Redes móveis que usam NAT de gateway de operadora (CGNAT ou CGN, na sigla em inglês)
  • TCP de vários caminhos (MPTCP)

Todos esses fatores podem contribuir para que um determinado cliente tenha um endereço IP não determinístico durante uma sessão de reprodução de vídeo. Se o endereço IP do cliente mudar depois que você emitir o acesso e o cliente tentar fazer o download de um segmento de vídeo no buffer de reprodução, ele vai receber um HTTP 403 da CDN de mídia.

IPRanges=BASE_64_IP_RANGES

SessionID

id

Uma string arbitrária, útil para análise de registros ou rastreamento de reprodução.

Para evitar a criação de um token inválido, use strings codificadas em % ou codificadas em base64 seguras para a Web. Os caracteres a seguir não podem ser usados para SessionID, porque eles invalidam o token: "~", "&" ou " " (espaço).

SessionID=SESSION_ID_VALUE

Data

data, payload

Uma string arbitrária, útil para a análise de registros.

Para evitar a criação de um token inválido, use strings codificadas em % ou codificadas em base64 seguras para a Web. Os caracteres a seguir não podem ser usados para Data, porque eles invalidam o token: "~", "&" ou " " (espaço).

data=DATA_VALUE
Headers Uma lista delimitada por vírgulas de nomes de campos de cabeçalho. Os nomes de cabeçalho não diferenciam maiúsculas de minúsculas para pesquisas na solicitação. Os nomes de cabeçalho nos valores assinados diferenciam maiúsculas de minúsculas. Se um cabeçalho estiver ausente, o valor será a string vazia. Se houver várias cópias de um cabeçalho, elas serão concatenadas por vírgulas. Headers=HEADER_1_NAME=HEADER_1_EXPECTED_VALUE, HEADER_2_NAME=HEADER_2_EXPECTED_VALUE

Exemplos

As seções a seguir mostram exemplos de como gerar tokens.

Exemplo de uso de FullPath

Confira o exemplo a seguir usando o campo FullPath:

  • Item solicitado: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Tempo de expiração: 160000000

O valor assinado é:

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

Para criar um token, assine o valor assinado com uma assinatura Ed25519 ou uma HMAC de chave simétrica.

Confira abaixo exemplos de tokens criados com um valor assinado:

Assinatura Ed25519

Expires=160000000~FullPath~Signature=SIGNATURE_OF_SIGNED_VALUE

Em que SIGNATURE_OF_SIGNED_VALUE é a assinatura ED25519 do valor assinado criado anteriormente.

HMAC de chave simétrica

Expires=160000000~FullPath~hmac=HMAC_OF_SIGNED_VALUE

em que HMAC_OF_SIGNED_VALUE é o HMAC de chave simétrica do valor assinado criado anteriormente.

Nos exemplos anteriores, FullPath é fornecido no token, mas o valor não é repetido do caminho especificado no valor assinado. Isso permite que você assinale o caminho completo da solicitação sem duplicar a solicitação no token.

Exemplo de uso de URLPrefix

Confira o exemplo a seguir usando o campo URLPrefix:

  • Item solicitado: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Tempo de expiração: 160000000

O valor assinado é:

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4

No exemplo anterior, substituímos o caminho para o item solicitado, http://example.com/tv/my-show/s01/e01/playlist.m3u8, pelo caminho para o item no formato Base64 seguro para a Web, aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4.

Para criar um token, assine o valor assinado com uma assinatura Ed25519 ou uma HMAC de chave simétrica.

Confira abaixo exemplos de tokens criados com um valor assinado:

Assinatura Ed25519

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4~Signature=SIGNATURE_OF_SIGNED_VALUE

Em que SIGNATURE_OF_SIGNED_VALUE é a assinatura ED25519 do valor assinado criado anteriormente.

HMAC de chave simétrica

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4~hmac=HMAC_OF_SIGNED_VALUE

em que HMAC_OF_SIGNED_VALUE é o HMAC de chave simétrica do valor assinado criado anteriormente.

Exemplo de uso de Headers

Confira o exemplo a seguir usando o campo Headers:

  • Item solicitado: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Tempo de expiração: 160000000
  • Valor de PathGlobs: *
  • Cabeçalhos de solicitação esperados:
    • user-agent: browser
    • accept: text/html

O valor assinado é:

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

Para criar um token, assine o valor assinado com uma assinatura Ed25519 ou uma HMAC de chave simétrica.

Confira abaixo exemplos de tokens criados com um valor assinado:

Assinatura Ed25519

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

Em que SIGNATURE_OF_SIGNED_VALUE é a assinatura ED25519 do valor assinado criado anteriormente.

HMAC de chave simétrica

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

em que HMAC_OF_SIGNED_VALUE é o HMAC de chave simétrica do valor assinado criado anteriormente.

Nos exemplos anteriores, Headers=user-agent,accept é fornecido no token, mas os valores de cabeçalho esperados não são repetidos do valor assinado. Isso permite assinar pares de chave-valor específicos do cabeçalho de solicitação sem duplicar os valores no token.