昨天解析出的 Media playlist,除了片段清單,有時候還會帶一個 #EXT-X-KEY 標籤,宣告這份清單的片段是加密過的。第一次遇到會有個直覺疑問:加密的內容要解密,金鑰在哪裡?答案是:清單本身會告訴你去哪裡拿金鑰,但初始向量(IV)不一定會明講。
#EXT-X-KEY 標籤帶的資訊#EXT-X-KEY 標籤長這樣#EXT-X-KEY:METHOD=AES-128,URI="https://example.com/key",IV=0x0000000000000000000000000000f0
#EXTINF:8.0,
segment000.ts
METHOD 宣告加密演算法(AES-128 是 HLS 最常見的加密方式,使用 CBC 模式);URI 是取得金鑰本身的位置——金鑰不是寫在清單裡,而是要另外發一次請求去下載;IV 則是這次加密使用的初始向量,用十六進位字串表示。
規格(RFC 8216)定義:如果 #EXT-X-KEY 沒有帶 IV 屬性,就要用這個片段的媒體序號(media sequence number)當作 IV,把序號轉成對應長度的位元組序列來使用。這代表解密邏輯不能假設「一定拿得到 IV 字串」,要準備好在沒有明講時,用片段序號推算出對應的 IV。
❌ 反例:假設清單一定會帶 IV
def get_cipher(key: bytes, key_tag) -> AES:
iv = bytes.fromhex(key_tag.iv.replace("0x", "")) # 沒有 IV 時直接拋例外
return AES.new(key, AES.MODE_CBC, iv)
✅ 正例:沒有明講時,退回用媒體序號推算
def get_cipher(key: bytes, key_tag, sequence: int) -> AES:
if key_tag.iv is not None:
iv = bytes.fromhex(key_tag.iv.replace("0x", ""))
else:
iv = sequence.to_bytes(16, byteorder="big") # 規格定義的退路
return AES.new(key, AES.MODE_CBC, iv)
如果你處理過某種加密協定,協定裡有沒有「某個參數在特定情況下可以省略、規格定義了預設推算方式」這種細節?你的實作有沒有處理省略的情況,還是只驗證過「有明講」的那條路徑?
#EXT-X-KEY 宣告加密方式跟金鑰位置,金鑰本身要另外發請求取得明天是一個真實踩過的坑:解密結果是亂碼,問題出在 IV 沒處理對。