生成签名

本指南介绍了如何创建签名,以及签名的必需字段和可选字段。

如需创建签名,您需要构建要签名的字符串,我们在本指南中将其称为已签名值。带符号的值包含描述以下内容的参数: 您要保护的内容、带符号值的过期时间等 。

您可以在创建签名字符串时使用已签名值。您创建了一个 通过组合签名参数来获得签名字符串,例如 签名值的非对称密钥 Ed25519 签名。

Media CDN 使用最终的组合签名来帮助保护您的内容。

支持的签名格式

Media CDN 支持以下已签名请求格式。

格式 行为 示例
查询参数(确切网址)

确切网址,用于授予对特定网址的访问权限。

完全匹配:

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

查询参数(网址前缀) 通过指定 URLPrefix,您可以对前缀进行签名,并将相同的查询参数附加到播放器或清单生成过程中的多个网址。

签名内容:

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

PREFIX 替换为要授予访问权限的前缀,包括架构、主机和部分路径。

路径组件

前缀:允许访问前缀在 "/edge-cache-token=[...]" 组件。

这样一来,在提取子资源时,相对清单网址便可自动继承签名网址组件。

https://media.example.com/video/edge-cache-token=Expires=EXPIRATION
&KeyName=KEY_NAME
&Signature=SIGNATURE/manifest_12382131.m3u8
签名 Cookie 前缀:Cookie 允许访问具有指定前缀的任何网址 带符号的 URLPrefix 值。

Edge-Cache-Cookie:

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

创建签名

  1. 通过串联包含必需的签名字段和所需可选的签名字段的字符串来创建已签名值。

    如果指定,则 URLPrefix 必须在前面,后跟 ExpiresKeyName。 以及任何可选参数

    请使用以下内容分隔每个字段和所有参数:

    • 对于 Cookie,请使用英文冒号 : 字符。
    • 对于查询参数和路径组成部分,请使用“&”符号 & 个字符。
  2. 使用 Ed25519 签名对有符号的值进行签名。

  3. 附加字段分隔符(:&) 后跟 Signature= 和 Ed25519 签名,添加到字符串的末尾。

创建签名网址

以下代码示例展示了如何以编程方式创建签名网址。

Go

如需向媒体 CDN 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

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

如需向媒体 CDN 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

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

创建签名网址前缀

以下代码示例展示了如何以编程方式创建 签名网址前缀。

Go

如需向媒体 CDN 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

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

如需向 Media CDN 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

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

以下代码示例展示了如何以编程方式创建签名网址 Cookie。

创建签名路径组件

以下代码示例展示了如何以编程方式创建签名路径 组件。

Python

如需向 Media CDN 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

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

必需的签名字段

每个签名都必须包含以下字段:

  • Expires
  • KeyName
  • Signature

如果存在查询参数,它们必须归入最后一个 参数。除非另有说明,否则参数名称及其 值区分大小写。

下表介绍了各个参数:

字段名称 签名参数 有符号值
Expires 自 Unix 纪元以来经过的整数秒数 (1970-01-01T00:00:00Z) Expires=EXPIRATION_TIME 之后, 签名不再有效。
KeyName 用于对此请求进行签名的 EdgeCacheKeyset 的名称。KeyName 是指整个密钥集,而不是密钥集本身中的各个密钥。 KeyName=EDGE_CACHE_KEYSET
Signature 签名的 base-64 编码版本。 不适用

可选签名字段

如果存在查询参数,则必须将其作为网址中的最后一个参数分组在一起。除非另有说明,否则参数名称及其 值区分大小写。

下表介绍了每个参数的名称和可选参数的详细信息 签名参数:

字段名称 签名参数 有符号值
HeaderName

已命名的请求标头字段名称,该名称必须出现在 请求。

签名时必须小写,因为标头字段名称是 区分大小写。Media CDN 会先将标头转换为小写,然后再验证签名。

HeaderName=HEADER_NAME
HeaderValue 已命名的请求标头字段值,该值必须出现在 请求。通常为用户 ID 或其他不透明标识符。包含 HeaderValue 但不包含 HeaderName 的请求会被拒绝。 HeaderValue=HEADER_VALUE
IPRanges

最多五个 IPv4 和 IPv6 地址的 CIDR 格式列表,此网址以网络安全的 base64 格式有效。例如: 指定 IP 范围“192.6.13.13/32,193.5.64.135/32”,则您将其指定为 IPRanges=MTkyLjYuMTMuMTMvMzIsMTkzLjUuNjQuMTM1LzMy

当客户端 或连接网络路径 应用前端不同于交付路径。 如果客户端使用未包含在签名请求中的 IP 地址进行连接,Media CDN 会使用 HTTP 403 代码拒绝该客户端。

以下情况可能会导致媒体 CDN 使用 HTTP 403 代码拒绝客户端:

  • 双栈(IPv4、IPv6)环境
  • 连接迁移(从 Wi-Fi 到移动网络,以及从移动网络到 Wi-Fi)
  • 使用运营商网关 NAT (CGNAT 或 CGN) 的移动网络
  • 多路径 TCP (MPTCP)

所有这些因素都可能会导致给定客户端在视频播放会话期间具有非确定性 IP 地址。如果 但在您授予访问权限后,客户端 IP 地址会发生变化, 然后,客户端尝试将视频片段下载到其播放 它们会从 HTTP 403 接收 媒体 CDN。

IPRanges=BASE_64_IP_RANGES
URLPrefix 要授予访问权限的 base64(网址安全)网址前缀。指定一个 URLPrefix 可让您对前缀进行签名并附加相同的查询 参数添加到播放器或清单生成中的多个网址。 使用签名 Cookie 时,必须提供 URLPrefix 格式。 URLPrefix=BASE_64_URL_PREFIX