下载的 m3u8 是加密的?手把手解密 AES-128 并转为本地可播视频
从某些站点扒下来的 m3u8,合并 TS 后却是一堆乱码、播放器报"无法解码"。十有八九是 AES-128 加密——m3u8 文件里用 #EXT-X-KEY 指明了密钥位置和方式,这些 TS 切片是用同一把密钥加过密的。这篇文章把加密原理、密钥获取、逐片解密、批量本地化、以及常见坑一次讲透,让你能把加密 m3u8 变成普通的本地视频。
一、m3u8 里的加密长什么样
打开 m3u8,你会看到类似:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-KEY:METHOD=AES-128,URI="key.key",IV=0x1a2b3c4d5e6f70819283746556473829
#EXTINF:4.0,
seg0.ts
#EXTINF:4.0,
seg1.ts
含义:
METHOD=AES-128:用 AES-128 的 CBC 模式加密每个 TS 切片;URI:密钥文件地址(16 字节);IV:初始化向量(16 字节),没有的话默认用分片序号当 IV。
注意:这是"流加密"级别的防护,密钥就明晃晃写在 m3u8 里(或能直接下载),所以它防的是"直接右键另存为拿到明文",不是真正的版权保护(那是 Widevine 的活)。
二、第一步:拿到密钥
密钥通常和 m3u8 同目录,相对路径拼上基地址下载:
curl -O https://example.com/path/key.key
确认它正好 16 字节:wc -c key.key 应显示 16。若 URI 是远程且需要鉴权(带 cookie/referer),下载 key 时一并带上对应请求头。
三、第二步:逐片解密(openssl)
用 openssl 对每片 TS 解密:
openssl aes-128-cbc -d \
-in seg0.ts -out seg0_dec.ts \
-K $(xxd -p key.key) \
-iv 1a2b3c4d5e6f70819283746556473829
-K要密钥的十六进制串(不是文件本身),xxd -p key.key把二进制转十六进制;-iv是 IV 的十六进制;- 若 m3u8 没写 IV,通常 IV 等于分片序号的 16 进制(seg0 →
00000000000000000000000000000000,seg1 →00000000000000000000000000000001...)。
四、更省事:直接交给 ffmpeg
ffmpeg 能读带 #EXT-X-KEY 的 m3u8 并一步解密合并——只要密钥 URL 还能访问:
ffmpeg -i "https://example.com/path/index.m3u8" -c copy out.mp4
如果密钥是本地文件,把 m3u8 里的 URI 改成相对路径再喂给 ffmpeg 即可。
五、第三步:改 m3u8 指向本地,完全离线
把 m3u8 下载到本地,把所有远程前缀去掉/改成本地相对路径,TS 切片和 key 也下到同目录,就能离线播放和二次处理:
# 下载整个目录(假设静态托管)
wget -r -np -R "*.html" https://example.com/path/
# 批量清掉 m3u8 里的绝对路径
sed -i 's#https://example.com/path/##g' index.m3u8
# 解密合并
ffmpeg -i index.m3u8 -c copy local.mp4
六、批量自动化(Python 思路)
把"下载 key + 下载所有 ts + 解密 + 合并"串成脚本:
import requests, subprocess, os
base = 'https://example.com/path/'
key = requests.get(base + 'key.key').content # 16 字节
open('key.bin', 'wb').write(key)
# 下载 m3u8,解析出 ts 列表
m3u8 = requests.get(base + 'index.m3u8').text
segs = [l for l in m3u8.splitlines() if l and not l.startswith('#')]
for i, seg in enumerate(segs):
r = requests.get(base + seg)
open(f'seg_{i}.ts', 'wb').write(r.content)
# 用 ffmpeg 一次性解密合并(key 放同目录,改 m3u8 URI 为 key.bin)
...
熟练后这就是一个迷你下载器。
七、SAMPLE-AES 说明
除了 AES-128(整片加密),还有 SAMPLE-AES:只加密每个样本的部分(如视频的 nalu 载荷),音频不加密。这种处理复杂得多,需要专门工具(如 ffmpeg 配合支持的解密模块或 shaka/ExoPlayer),openssl 逐片解不适用。遇到 #EXT-X-KEY:METHOD=SAMPLE-AES 别用本文方法。
八、常见翻车现场
| 现象 | 根因 | 解法 |
|---|---|---|
| 解密后仍是乱码 | IV 不对 | 试分片序号当 IV,或检查 key 是否 16 字节 |
| ffmpeg 报找不到 key | URI 仍是远程且失效 | 下到本地改 m3u8 |
| 只有第一片能播 | 每片 IV 不同却用了同一个 | 按 m3u8 里每片 IV 分别解 |
| key 文件要鉴权 | 需带 cookie/referer | 下载 key 时带上对应头 |
| 全片黑屏有声音 | 只解密了视频或顺序错 | 确认按 m3u8 顺序合并 |
九、常见问题
Q:解密等于破解 DRM 吗? 不等于。AES-128 在 m3u8 里是明文传密钥,只是防直接下载,能拿到密钥说明它没做真正的版权保护(那是 Widevine 的事)。
Q:解密后能再加密吗? 可以,用 shaka-packager(之前写过)重新打包成带 Clear Key 或 Widevine 保护的格式。
Q:为什么有的 m3u8 没 EXT-X-KEY 但下下来还是乱码? 可能是 SAMPLE-AES,或 TS 本身被其他处理,需具体分析。
Q:能边下边解密吗? 能,下载完每个 ts 立即 openssl 解密再追加到输出文件,省去中间落地。
小结
加密 m3u8 不可怕:找到 #EXT-X-KEY 拿到 16 字节 key 和 IV,用 openssl 或 ffmpeg 逐片/一次性解密,再合并成普通视频即可。记住它防的是"随手下载",不是真版权——真正的高级保护在 Widevine 那一层。