產生簽名

本指南說明如何建立簽章,以及簽章的必填和選填欄位。

如要建立簽章,請編寫要簽署的字串,在本指南中稱為「簽署值」。簽署的值包含描述受保護內容的參數、簽署值的到期時間等。

建立簽章字串時,請使用已簽署的值。您可透過組合簽章參數 (例如簽署值的非對稱金鑰 Ed25519 簽章),建立簽章字串。

媒體 CDN 會使用最終組成的簽章,協助保護您的內容。

支援的簽署格式

媒體 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,則必須先指定 URLPrefix,接著依序指定 ExpiresKeyName,然後再指定任何選用參數。

    請使用下列符號分隔各個欄位和參數:

    • 如果是 Cookie,請使用冒號 : 字元。
    • 查詢參數和路徑元件請使用連字號 & 字元。
  2. 使用 Ed25519 簽章簽署已簽署的值。

  3. 在字串結尾附加欄位分隔符 (:&) ,然後附加 Signature= 和 Ed25519 簽章。

建立已簽署的網址

下列程式碼範例說明如何以程式輔助方式建立已簽署網址。

Go

如要向 Media 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

如要向 Media 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

如要向 Media 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 簽章的 Base64 編碼版本。 不適用

選填簽名欄位

如有查詢參數,必須將其歸為網址中的最後一組參數。除非另有說明,否則參數名稱和值會區分大小寫。

下表說明選用簽章參數的名稱和詳細資料:

欄位名稱 簽名參數 簽署的值
HeaderName

要求中必須出現的具名要求標頭欄位名稱。

簽署時必須使用小寫,因為標頭欄位名稱區分大小寫。Media CDN 會先將標頭轉換為小寫,再驗證簽章。

HeaderName=HEADER_NAME
HeaderValue 要求中必須出現的具名要求標頭欄位值。這通常是使用者 ID 或其他不透明的 ID。系統會拒絕含有 HeaderValue 但沒有 HeaderName 的要求。 HeaderValue=HEADER_VALUE
IPRanges

最多五個以 CIDR 格式表示的 IPv4 和 IPv6 位址清單,這些位址的網址以網頁安全 Base64 格式表示。舉例來說,如要指定 IP 範圍「192.6.13.13/32,193.5.64.135/32」,請指定 IPRanges=MTkyLjYuMTMuMTMvMzIsMTkzLjUuNjQuMTM1LzMy

如果用戶端有 WAN 遷移的風險,或是應用程式前端的網路路徑與傳送路徑不同,則在簽章中加入 IPRanges 可能沒有幫助。如果用戶端連線時使用的 IP 位址不在已簽署的要求中,Media CDN 會以 HTTP 403 程式碼拒絕用戶端。

在下列情況下,Media CDN 可能會以 HTTP 403 代碼拒絕用戶端:

  • 雙重堆疊 (IPv4、IPv6) 環境
  • 連線遷移 (從 Wi-Fi 遷移至行動網路,以及從行動網路遷移至 Wi-Fi)
  • 使用電信業者閘道 NAT (CGNAT 或 CGN) 的行動網路
  • 多路徑 TCP (MPTCP)

這些因素都可能導致特定用戶端在影片播放期間擁有非決定性 IP 位址。如果發出存取權後用戶端 IP 位址變更,且用戶端嘗試將影片片段下載至播放緩衝區,則會收到 Media CDN 傳送的 HTTP 403

IPRanges=BASE_64_IP_RANGES
URLPrefix 要授予存取權的 base64 (網址安全) 網址前置字串。指定 URLPrefix 可讓您簽署前置字元,並在播放器或資訊清單產生期間,將相同的查詢參數附加至多個網址。使用已簽署的 Cookie 格式時,必須提供 URLPrefix URLPrefix=BASE_64_URL_PREFIX