Gerar tokens

Este guia explica como gerar um token e as etapas obrigatórias e para tokens.

Para criar um token, escreva uma string a ser assinada, que chamamos de usuário valor neste guia. O valor assinado inclui parâmetros que descrevem conteúdo protegido, o tempo de expiração do valor assinado, e para a frente.

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

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

Criar um token

  1. Crie um valor assinado concatenando uma string que contém o campos de token obrigatórios e token opcional opcional . Separe cada campo e quaisquer parâmetros com um caractere til ~.

  2. Assine o valor assinado com uma assinatura Ed25519 ou uma chave simétrica HMAC (link em inglês).

  3. Componha o token concatenando uma string que contém o token campos de token e campos de token opcionais. Separe cada campo com um caractere til ~.

    Ao compor o token, os valores de cada parâmetro são os entre o valor assinado e a string do token, com o seguinte exceções:

    • FullPath
    • Headers

O exemplo de código a seguir 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 pelos tokens.

Campos de token obrigatórios

Os campos a seguir são obrigatórios para cada token:

  • 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 seus valores diferenciam maiúsculas de minúsculas.

A tabela a seguir explica cada parâmetro:

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

Expires

exp

Segundos inteiros que passaram desde a época do Unix (1970-01-01T00:00:00Z) Expires=EXPIRATION_TIME. Depois disso, token não é mais válido.

PathGlobs

paths, acl

Uma lista de até cinco segmentos de caminho aos quais conceder acesso. Os segmentos pode ser delimitado por vírgulas (,) ou pontos de exclamação (!), mas não as duas opções.

PathGlobs aceita caracteres curinga nos caminhos usando asteriscos (*) e pontos de interrogação (?). Um único o caractere asterisco (*) abrange qualquer número de segmentos de caminho, ao contrário da sintaxe de correspondência de padrões de pathMatchTemplate.

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

Por esses motivos, seu URL não pode conter os seguintes caracteres especiais: ,!*?;

PathGlobs=PATHS
URLPrefix

Um URL codificado em base64 seguro para a Web que inclui o protocolo http:// ou https:// até um ponto do seu de sua escolha.

Por exemplo, alguns valores de URLPrefix válidos 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 campo sem =. FullPath=FULL_PATH_TO_OBJECT
Signature Uma versão da assinatura codificada em base64 e segura para a Web. Não aplicável
hmac Uma versão codificada em base64 segura para a Web do valor HMAC. Não aplicável

Sintaxe de caractere curinga PathGlobs

A tabela a seguir explica a sintaxe de caractere curinga PathGlobs.

Operador Corresponde a Exemplos
* (asterisco) Corresponde a zero ou mais caracteres no caminho do URL, incluindo (/).
  • /videos/* corresponde a qualquer caminho que começa com /videos/.
  • /videos/s*/4k/* corresponde a /videos/s/4k/ e /videos/s01/4k/main.m3u8
  • /manifests/*/4k/* correspondências /manifests/s01/4k/main.m3u8 e /manifests/s01/e01/4k/main.m3u8 Não corresponde /manifests/4k/main.m3u8:
? (ponto de interrogação) Corresponde a um único caractere em o caminho do URL, sem incluir a barra (/) caracteres. /videos/s?main.m3u8 correspondências /videos/s1main.m3u8. Ele não corresponde a nenhum dos /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 usando qualquer um dos seus tokens assinados. Para máxima proteção, verifique se os globs correspondem ao conteúdo ao qual você pretende conceder acesso.

Campos de token opcionais

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

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

Nome / aliases do campo Parâmetros Valor assinado

Starts

st

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

Uma lista de até cinco endereços IPv4 e IPv6 no formato CIDR para em que o 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:

Pode não ser útil incluir IPRanges em tokens quando os clientes estão correndo risco de migrações de WAN ou casos em que o caminho de rede front-end do aplicativo é diferente do caminho de entrega. O Media CDN rejeita clientes com um HTTP 403 ao se conectarem a um endereço IP que não faz parte da solicitação assinada.

Confira a seguir casos que podem resultar no Media CDN Rejeite clientes com um código HTTP 403:

  • Ambientes de pilha dupla (IPv4, IPv6)
  • Migração da conexão (Wi-Fi para celular e celular para Wi-Fi)
  • Redes móveis que usam Carrier Gateway NAT (CGNAT ou CGN)
  • TCP de vários caminhos (MPTCP)

Todos esses fatores podem contribuir para que um determinado cliente um endereço IP não determinista durante uma sessão de reprodução de vídeo. Se o o endereço IP do cliente for alterado depois que você conceder acesso, e o o cliente tenta baixar um trecho de vídeo nos vídeos buffer, eles recebem um HTTP 403 Media CDN do Google Cloud.

IPRanges=BASE_64_IP_RANGES

SessionID

id

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

Para evitar a criação de um token inválido, use a codificação % ou um token seguro para a Web strings codificadas em base64. Os caracteres a seguir não devem ser usados para SessionID, porque fazem com que o token seja inválido: "~", "&" ou " " (espaço).

SessionID=SESSION_ID_VALUE

Data

data, payload

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

Para evitar a criação de um token inválido, use a codificação % ou um token seguro para a Web strings codificadas em base64. Os caracteres a seguir não devem ser usados para Data, porque fazem com que o token seja inválido: "~", "&" 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 são não diferencia maiúsculas de minúsculas para as pesquisas feitas na solicitação. Os nomes de cabeçalho na versão diferenciam maiúsculas de minúsculas. Se um cabeçalho estiver ausente, o valor será o fio. Se houver várias cópias de um cabeçalho, elas serão concatenados por vírgula. Headers=HEADER_1_NAME=HEADER_1_EXPECTED_VALUE, HEADER_2_NAME=HEADER_2_EXPECTED_VALUE

Exemplos

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

Exemplo de uso de FullPath

Considere o exemplo a seguir usando o campo FullPath:

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

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 com uma chave simétrica para HMAC.

Confira a seguir 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 da 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 anterior.

Nos exemplos anteriores, FullPath é fornecido no token, mas o valor não é repetido a partir do caminho especificado no valor assinado. Isso permite que você assinar o caminho completo da solicitação sem duplicá-la no token.

Exemplo de uso de URLPrefix

Considere o exemplo a seguir usando o campo URLPrefix:

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

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. em formato Base64 seguro para a Web, aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4.

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

Confira a seguir 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 da 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 anterior.

Exemplo de uso de Headers

Considere o exemplo a seguir usando o campo Headers:

  • Item solicitado: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Tempo de expiração: 16.000.000
  • 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 com uma chave simétrica para HMAC.

Confira a seguir 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 da 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 anterior.

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