Gere assinaturas

Este guia explica como criar uma assinatura e os campos obrigatórios e opcionais para assinaturas.

Para criar uma assinatura, compõe uma string para assinar, à qual nos referimos como um valor assinado neste guia. O valor assinado inclui parâmetros que descrevem o conteúdo que está a proteger, a hora de validade do valor assinado e assim sucessivamente.

Usa o valor assinado ao criar uma string de assinatura. Cria uma string de assinatura ao compor os parâmetros da assinatura, como uma assinatura Ed25519 de chave assimétrica do valor assinado.

A RFC usa a assinatura composta final para ajudar a proteger o seu conteúdo.

Formatos de assinatura suportados

A RFC 2616 define o formato de pedido assinado.

Formato Comportamento Exemplo
Parâmetros de consulta (URL exato)

URL exato, para conceder acesso a um URL específico.

Exata:

https://media.example.com/content/manifest.m3u8?
Expires=EXPIRATION
&KeyName=KEY_NAME
&Signature=SIGNATURE

Parâmetros de consulta (prefixo do URL) A especificação de um URLPrefix permite-lhe assinar um prefixo e anexar os mesmos parâmetros de consulta a vários URLs na geração do seu leitor ou manifesto.

O que deve assinar:

URLPrefix=PREFIX
&Expires=EXPIRATION
&KeyName=KEY_NAME
&Signature=SIGNATURE

Substitua PREFIX pelo prefixo ao qual quer conceder acesso, incluindo o esquema, o anfitrião e o caminho parcial.

Componente de caminho

Prefixo: permite o acesso a qualquer URL com um prefixo antes do componente "/edge-cache-token=[...]".

Isto permite que os URLs de manifesto relativos herdem automaticamente o componente de URL assinado quando obtêm sub-recursos.

https://media.example.com/video/edge-cache-token=Expires=EXPIRATION
&KeyName=KEY_NAME
&Signature=SIGNATURE/manifest_12382131.m3u8
Cookie assinado Prefixo: o cookie permite o acesso a qualquer URL com o prefixo especificado no valor URLPrefix assinado.

Edge-Cache-Cookie:

URLPrefix=PREFIX:
Expires=EXPIRATION:
KeyName=KEY_NAME:
Signature=SIGNATURE

Crie uma assinatura

  1. Crie um valor assinado concatenando uma string que contenha os campos de assinatura obrigatórios e os campos de assinatura opcionais pretendidos.

    Se especificado, URLPrefix tem de aparecer primeiro, seguido de Expires, KeyName e, em seguida, quaisquer parâmetros opcionais.

    Separe cada campo e todos os parâmetros com o seguinte:

    • Para cookies, use um caráter de dois pontos :.
    • Para parâmetros de consulta e componentes de caminho, use um caráter & de "E" comercial.
  2. Assine o valor assinado com uma assinatura Ed25519.

  3. Anexe um separador de campos (: ou &), seguido de Signature= e da assinatura Ed25519 ao final da string.

Crie um URL assinado

Os seguintes exemplos de código mostram como criar programaticamente um URL assinado.

Go

Para se autenticar na RFC de multimédia, configure as Credenciais padrão da aplicação. Para mais informações, consulte o artigo Configure a autenticação para um ambiente de desenvolvimento local.

import (
	"crypto/ed25519"
	"encoding/base64"
	"fmt"
	"io"
	"strings"
	"time"
)

// signURL prints the signed URL string for the specified URL and configuration.
func signURL(w io.Writer, url, keyName string, privateKey []byte, expires time.Time) error {
	// url := "http://example.com"
	// keyName := "your_key_name"
	// privateKey := "[]byte{34, 31, ...}"
	// expires := time.Unix(1558131350, 0)

	sep := '?'
	if strings.ContainsRune(url, '?') {
		sep = '&'
	}
	toSign := fmt.Sprintf("%s%cExpires=%d&KeyName=%s", url, sep, expires.Unix(), keyName)
	sig := ed25519.Sign(privateKey, []byte(toSign))

	fmt.Fprintf(w, "%s&Signature=%s", toSign, base64.RawURLEncoding.EncodeToString(sig))

	return nil
}

Python

Para se autenticar na RFC de multimédia, configure as Credenciais padrão da aplicação. Para mais informações, consulte o artigo Configure a autenticação para um ambiente de desenvolvimento local.

import base64
import datetime

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


from six.moves import urllib

def sign_url(
    url: str, key_name: str, base64_key: str, expiration_time: datetime.datetime
) -> str:
    """Gets the Signed URL string for the specified URL and configuration.

    Args:
        url: URL to sign as a string.
        key_name: name of the signing key as a string.
        base64_key: signing key as a base64 encoded byte string.
        expiration_time: expiration time as a UTC datetime object.

    Returns:
        Returns the Signed URL appended with the query parameters based on the
        specified configuration.
    """
    stripped_url = url.strip()
    parsed_url = urllib.parse.urlsplit(stripped_url)
    query_params = urllib.parse.parse_qs(parsed_url.query, keep_blank_values=True)
    epoch = datetime.datetime.utcfromtimestamp(0)
    expiration_timestamp = int((expiration_time - epoch).total_seconds())
    decoded_key = base64.urlsafe_b64decode(base64_key)

    url_pattern = "{url}{separator}Expires={expires}&KeyName={key_name}"

    url_to_sign = url_pattern.format(
        url=stripped_url,
        separator="&" if query_params else "?",
        expires=expiration_timestamp,
        key_name=key_name,
    )

    digest = ed25519.Ed25519PrivateKey.from_private_bytes(decoded_key).sign(
        url_to_sign.encode("utf-8")
    )
    signature = base64.urlsafe_b64encode(digest).decode("utf-8")
    signed_url = "{url}&Signature={signature}".format(
        url=url_to_sign, signature=signature
    )

    return signed_url

Crie um prefixo de URL assinado

Os exemplos de código seguintes mostram como criar programaticamente um prefixo de URL assinado.

Go

Para se autenticar na RFC de multimédia, configure as Credenciais padrão da aplicação. Para mais informações, consulte o artigo Configure a autenticação para um ambiente de desenvolvimento local.

import (
	"crypto/ed25519"
	"encoding/base64"
	"fmt"
	"io"
	"strings"
	"time"
)

// signURLPrefix prints the signed URL string for the specified URL prefix and configuration.
func signURLPrefix(w io.Writer, urlPrefix, keyName string, privateKey []byte, expires time.Time) error {
	// urlPrefix := "https://examples.com"
	// keyName := "your_key_name"
	// privateKey := "[]byte{34, 31, ...}"
	// expires := time.Unix(1558131350, 0)

	sep := '?'
	if strings.ContainsRune(urlPrefix, '?') {
		sep = '&'
	}

	toSign := fmt.Sprintf(
		"URLPrefix=%s&Expires=%d&KeyName=%s",
		base64.RawURLEncoding.EncodeToString([]byte(urlPrefix)),
		expires.Unix(),
		keyName,
	)
	sig := ed25519.Sign(privateKey, []byte(toSign))

	fmt.Fprintf(
		w,
		"%s%c%s&Signature=%s",
		urlPrefix,
		sep,
		toSign,
		base64.RawURLEncoding.EncodeToString(sig),
	)

	return nil
}

Python

Para se autenticar na RFC de multimédia, configure as Credenciais padrão da aplicação. Para mais informações, consulte o artigo Configure a autenticação para um ambiente de desenvolvimento local.

import base64
import datetime

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


from six.moves import urllib

def sign_url_prefix(
    url: str,
    url_prefix: str,
    key_name: str,
    base64_key: str,
    expiration_time: datetime.datetime,
) -> str:
    """Gets the Signed URL string for the specified URL prefix and configuration.

    Args:
        url: URL of request.
        url_prefix: URL prefix to sign as a string.
        key_name: name of the signing key as a string.
        base64_key: signing key as a base64 encoded string.
        expiration_time: expiration time as a UTC datetime object.

    Returns:
        Returns the Signed URL appended with the query parameters based on the
        specified URL prefix and configuration.
    """
    stripped_url = url.strip()
    parsed_url = urllib.parse.urlsplit(stripped_url)
    query_params = urllib.parse.parse_qs(parsed_url.query, keep_blank_values=True)
    encoded_url_prefix = base64.urlsafe_b64encode(
        url_prefix.strip().encode("utf-8")
    ).decode("utf-8")
    epoch = datetime.datetime.utcfromtimestamp(0)
    expiration_timestamp = int((expiration_time - epoch).total_seconds())
    decoded_key = base64.urlsafe_b64decode(base64_key)

    policy_pattern = (
        "URLPrefix={encoded_url_prefix}&Expires={expires}&KeyName={key_name}"
    )
    policy = policy_pattern.format(
        encoded_url_prefix=encoded_url_prefix,
        expires=expiration_timestamp,
        key_name=key_name,
    )

    digest = ed25519.Ed25519PrivateKey.from_private_bytes(decoded_key).sign(
        policy.encode("utf-8")
    )
    signature = base64.urlsafe_b64encode(digest).decode("utf-8")
    signed_url = "{url}{separator}{policy}&Signature={signature}".format(
        url=stripped_url,
        separator="&" if query_params else "?",
        policy=policy,
        signature=signature,
    )
    return signed_url

Os exemplos de código seguintes mostram como criar programaticamente um cookie de URL assinado.

Crie um componente de caminho assinado

Os exemplos de código seguintes mostram como criar programaticamente um componente de caminho assinado.

Python

Para se autenticar na RFC de multimédia, configure as Credenciais padrão da aplicação. Para mais informações, consulte o artigo Configure 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_path_component(
    url_prefix: str,
    filename: str,
    key_name: str,
    base64_key: str,
    expiration_time: datetime.datetime,
) -> str:
    """Gets the Signed URL string for the specified URL prefix and configuration.

    Args:
        url_prefix: URL Prefix to sign as a string.
        filename: The filename of the sample request
        key_name: The name of the signing key as a string.
        base64_key: The signing key as a base64 encoded string.
        expiration_time: Expiration time as a UTC datetime object with timezone.

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

    expiration_duration = expiration_time.astimezone(
        tz=datetime.timezone.utc
    ) - datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc)
    decoded_key = base64.urlsafe_b64decode(base64_key)

    policy_pattern = "{url_prefix}edge-cache-token=Expires={expires}&KeyName={key_name}"
    policy = policy_pattern.format(
        url_prefix=url_prefix,
        expires=int(expiration_duration.total_seconds()),
        key_name=key_name,
    )

    digest = ed25519.Ed25519PrivateKey.from_private_bytes(decoded_key).sign(
        policy.encode("utf-8")
    )
    signature = base64_encoder(digest)

    signed_url = "{policy}&Signature={signature}/{filename}".format(
        policy=policy, signature=signature, filename=filename
    )

    return signed_url

Campos de assinatura obrigatórios

Os seguintes campos são obrigatórios para todas as assinaturas:

  • Expires
  • KeyName
  • Signature

Se existirem parâmetros de consulta, têm de ser agrupados como os últimos parâmetros no URL. Salvo especificação em contrário, os nomes dos parâmetros e os respetivos valores são sensíveis a maiúsculas e minúsculas.

A tabela seguinte explica cada parâmetro:

Nome do campo Parâmetros de assinatura Valor assinado
Expires Segundos inteiros decorridos desde o início da época Unix (1970-01-01T00:00:00Z) Expires=EXPIRATION_TIME, após a qual a assinatura deixa de ser válida.
KeyName O nome do EdgeCacheKeyset usado para assinar este pedido. KeyName refere-se ao conjunto de chaves completo e não às chaves individuais no próprio conjunto de chaves. KeyName=EDGE_CACHE_KEYSET
Signature Uma versão codificada em base64 da assinatura. Não aplicável

Campos de assinatura opcionais

Se existirem parâmetros de consulta, têm de ser agrupados como os últimos parâmetros no URL. Salvo especificação em contrário, os nomes dos parâmetros e os respetivos valores são sensíveis a maiúsculas e minúsculas.

A tabela seguinte explica o nome e os detalhes de cada parâmetro para os parâmetros de assinatura opcionais:

Nome do campo Parâmetros de assinatura Valor assinado
HeaderName

Um nome de campo de cabeçalho de pedido denominado que tem de estar presente no pedido.

Tem de estar em minúsculas quando assinado, porque os nomes dos campos de cabeçalho são sensíveis a maiúsculas e minúsculas. A CDN de multimédia converte o cabeçalho em minúsculas antes de validar a assinatura.

HeaderName=HEADER_NAME
HeaderValue Um valor de campo de cabeçalho de pedido com nome que tem de estar presente no pedido. Normalmente, trata-se de um ID do utilizador ou de outro identificador opaco. Os pedidos com HeaderValue, mas sem HeaderName, são rejeitados. HeaderValue=HEADER_VALUE
IPRanges

Uma lista de até cinco endereços IPv4 e IPv6 no formato CIDR para os quais este 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", especifica IPRanges=MTkyLjYuMTMuMTMvMzIsMTkzLjUuNjQuMTM1LzMy.

Os intervalos de IP podem não ser úteis para incluir em assinaturas quando os clientes estão em risco de migrações de WAN ou casos em que o caminho de rede para o frontend da sua aplicação é diferente do caminho de entrega. A RFC de multimédia rejeita clientes com um código HTTP 403 quando se ligam a um endereço IP que não faz parte do pedido assinado.

Seguem-se casos que podem resultar na rejeição de clientes pela RFC de multimédia com um código HTTP 403:

  • Ambientes de pilha dupla (IPv4 e IPv6)
  • Migração da ligação (Wi-Fi para rede móvel e rede móvel para Wi-Fi)
  • Redes móveis que usam a NAT de gateway do operador (CGNAT ou CGN)
  • TCP multipath (MPTCP)

Todos estes 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 de ter emitido o acesso e o cliente tentar transferir um segmento de vídeo para o respetivo buffer de reprodução, recebe um HTTP 403 da RFC de multimédia.

IPRanges=BASE_64_IP_RANGES
URLPrefix O prefixo de URL base64 (seguro para URL) para conceder acesso. A especificação de um URLPrefix permite-lhe assinar um prefixo e anexar os mesmos parâmetros de consulta a vários URLs na geração do manifesto ou do leitor. O elemento URLPrefix é obrigatório quando usa o formato de cookie assinado. URLPrefix=BASE_64_URL_PREFIX