关于加密,被问得最多的两句话是「你们用的什么算法」和「能不能被破解」。第一句可以用一个词回答,第二句永远只能回答「看对方愿意付出多少」。这篇不打算论证我们这套方案「安全」,而是把它的成立条件写清楚:在什么前提下有效、哪些条件下必然失效,以及为什么在课程视频这个场景里,这个取舍是合理的。
01先把问题问对
把「加密」当成一个开关,是绝大多数讨论跑偏的起点。在端侧播放这个场景里,加密做的事情只有一件:让内容以密文形式离开内容方,并在指定的一台机器上才变回明文。至于「算法够不够强」,只决定对手破解这把密钥需要多少算力;而对手真正想要的往往不是密钥,是明文——他可以从播放器手里拿,可以从屏幕里拍,也可以把整台机器搬走。
所以这篇要论证的命题不是「AES-256 很安全」,而是一个更窄、也更有用的判断:
在端侧播放场景里,防护强度的上限由「密钥在什么条件下才被算出来」决定,加密算法只决定下限。
后续四节都在验证这句话:先用威胁模型限定范围,再逐个排除公钥方案、无认证的对称模式、密钥随文件走、慢速 KDF,最后给出这套方案失效的条件02威胁模型:先限定这场论证的边界
没有威胁模型的加密讨论没有意义。课程视频这个场景里,能造成损失的行为只有四类,它们需要的「东西」并不相同。
| 对手 | 他要什么 | 他手上的资源 | 密码学能否影响 |
|---|---|---|---|
| A 转卖者 | 一份能播放的副本,或一份能解开的明文 | 加密包文件 | 能 |
| B 共享者 | 把自己的激活码给同学,让对方在他自己的电脑上播放 | 一枚有效激活码 | 能 |
| C 翻录者 | 屏幕上的画面与声音 | 一台能正常播放的机器 | 不能 |
| D 逆向者 | 密钥派生逻辑本身 | 播放器程序、样本文件 | 部分能(可提高成本) |
这张表先把 C 划出去:翻录不经过任何密钥,任何号称「防录屏」的软件方案都不是加密问题,而是显示层与系统权限问题,本文不讨论。剩下的 A、B、D 才是算法与密钥管理能起作用的范围。
03论证一:为什么不是公钥方案
「用不用非对称加密」是最常见的误判点。直觉上公钥体系更高级,但把它放到这个流程里,三条反证都成立。
反证一:公钥解决的是密钥传递,而这里的密钥传递本来就不需要走网络。非对称加密的原始动机,是让两个此前没有共享秘密的方在不安全信道上协商出一把对称密钥。我们的流程里,客户在自己的机器上读出机器码,通过微信发给内容方,内容方把这串码填进加密工具——这个环节没有任何"需要加密信道"的必要,因为机器码本身不是密钥,它只是一个设备标识。
反证二:公钥不能加密内容本身。以 RSA-2048 为例,PKCS#1 v1.5 填充下单次可加密的明文上限是 245 字节,连一帧视频都装不下。所以公钥方案在文件加密里只能干一件事:加密那把对称密钥。也就是说,内容仍然要靠 AES 逐段加密,公钥并没有替代对称算法,只是给"传密钥"换了个包装。
反证三:私钥必须落到客户端,暴露面等价。播放器要在客户机器上解密,就得持有能解开内容密钥的私钥。一旦播放器被逆向,私钥外流,效果与对称密钥外流完全一样;而代价是多了一层"每次解密先做一次非对称运算"的开销——在逐段解密 125 MB 视频的场景里,这笔开销没有换来任何额外保护。
结论:公钥方案在这个场景里不增加防护,只增加失败点(私钥保管、密钥轮换、每台机器的密钥对生成与分发)。它不是"更强",而是"解决另一个问题"。
04论证二:为什么必须是 AEAD,而不只是「加密」
选定对称之后,下一个决定是模式。这一步比"用多少位密钥"重要得多,因为课程视频有两个特殊性质:内容高度可预测(片头、字幕、常见转场),以及分发后可能被悄悄替换。
纯 CTR 或 CBC 模式只保证"看不懂",不保证"没被改"。CTR 的问题尤其直接:它把密钥流与明文异或,攻击者只要知道明文某个位置的原始字节,就能在密文的同一位置异或出任意改动,解密后明文就按他的意图变了——这在密码学里叫位翻转,不需要密钥,不需要算力。CBC 稍好,但整段替换、块重排仍然可能"解出看似正常的内容"。
AEAD(带认证的加密)要解决的正是这件事:密文后面跟一个认证标签,任何一位被改动,解密直接失败,而不是"解出一段坏画面"。我们的实现是 AES-256-GCM,每个文件独立一个 12 字节 nonce,密文后跟 16 字节认证标签,容器索引区也用同一套方式加密。
这里还有一个常被省略的细节:索引区也加密。很多「视频加密」方案只加密正文,文件名与目录结构裸着放,等于把课程大纲免费送出去。我们的实现里,索引用同一把密钥加密,播放器必须先通过密钥校验才能知道"里面有什么"。
05论证三:密钥从哪来,才是方案的分水岭
到了这一步,算法已经定完:对称、AES-256、GCM 模式。剩下的问题,也是真正决定这套方案是"改了后缀的压缩包"还是"带有设备绑定能力的加密交付",只有一句——密钥从哪里来。
候选方案有三个,逐个看它们的攻击面:
| 密钥来源 | 拿到文件后 | 服务端侧风险 | 换机表现 |
|---|---|---|---|
| 随机密钥写进文件头 | 直接可解,无需任何其他信息 | 无(本来就没秘密) | 可随意复制传播 |
| 服务端生成、授权后下发 | 需抓到下发链路或拖库 | 密钥库存量集中,拖库即全泄 | 可解,只要拿到同一枚密钥 |
| 由本机机器码派生 | 需知道目标机器的机器码 | 服务端从头到尾不持有密钥 | 自动失效(新机器算出的是另一把) |
我们选第三种:把机器码做一次 SHA256,取前 32 字节,作为 AES-256 的密钥。它同时解决三件事——服务端不存在密钥库,所以没有"拖库"这个风险;链路上没有"下发密钥"这个环节,所以抓包抓不到;换一台机器,算出来的密钥就不同,文件自动失效。
必须把这句话写全:派生方案的强度上限,等于机器码的保密程度。我们把机器码当作"设备标识"而不是"秘密",因为它是从磁盘序列号这类可枚举信息算出来的,不具备真正的随机性。所以真正的防线被转移到两处——机器码不轻易外流(授权记录里只存哈希),以及"能不能播放"依赖服务端校验。算法在这里没有增加任何保护。
06论证四:为什么派生用 SHA256,而不是 PBKDF2 / Argon2
看到"用一次 SHA256 当密钥派生",做过密码学的人第一反应通常是"这不够,应该用 Argon2id 加盐"。这个反应在口令场景里是对的,在设备场景里是错的——KDF 的选择由熵源决定,不由洁癖决定。
| 派生方式 | 设计意图 | 用在这里的后果 | 结论 |
|---|---|---|---|
| PBKDF2 / scrypt / Argon2id | 抬高每次猜测的计算成本,抵抗低熵口令的暴力破解 | 每次播放都要重算一遍密钥与索引,打开速度被直接拖慢;而机器码本身不可猜 | 不适用 |
| SHA256 直接派生 | 从高熵输入快速得到定长密钥 | 单次派生近乎零成本;安全性完全依赖机器码的不可枚举 | 适用 |
| 随机盐 + 慢哈希 | 防止同一输入在不同文件下得到同一密钥 | 盐必须随文件走,等于公开了"派生参数",仍挡不住已知机器码的对手 | 收益近似为零 |
机器码是 32 位十六进制,空间是 128 bit。慢哈希防的是"输入可能是 123456 或 password"这种情形,而 128 bit 随机空间下暴力枚举本身就不成立——每多花一秒算一次派生,只让正常用户的打开时间变长,不让攻击者多花一分钱。这是成本与收益的对账,不是对密码学建议的无视。
但这里有一条条件句,必须一并写出来:如果哪天密钥改由「用户自己设置的口令」派生,那么必须立刻换成 Argon2id 并加随机盐,还要限制尝试次数——因为那时熵源变成了人类记忆,性质完全不同。把这句话留在文章里,是为了让后来改代码的人知道边界在哪。
07论证五:52 字节头里的 HMAC 到底在干什么
容器格式是 .dat,头固定 52 字节,魔数 SLF1。这 52 字节里有 32 字节是 HMAC-SHA256。它经常被误认为"文件校验码",其实不是。
校验码的作用是发现"文件传坏了",用 CRC 或普通哈希就够;而这里要做的是在不解密任何内容之前,先证明这把密钥是对的。HMAC 需要密钥,攻击者不知道密钥就无法伪造出一个"看起来通过校验"的头——这正好让播放器能在第一毫秒就判断机器码对不对,而不是解出半段垃圾数据再报错。
顺带解释另一个设计:头本身不加密。因为它需要被加密工具识别、被版本标记区分,也需要让播放器在读第一块数据前就知道格式与偏移。把头加密,换来的安全感是零——头里除了魔数、版本、区段长度和那 32 字节 HMAC,没有任何内容信息。
08反过来论证:这套方案在什么条件下失效
论证一个方案,最有说服力的部分不是它能做什么,而是它明确做不到什么。以下四条都会让防护失效,我们逐条承认。
四种失效条件
- 明文临时文件。解码器只认普通视频格式,所以播放器必须先把密文解成明文交给解码器。任何一个在这台机器上有足够权限的人,都能在播放时抓到那份临时文件。
- 录屏与翻拍。画面一旦被渲染到屏幕,就不再经过密钥。这条路与加密无关,只能靠水印、账号溯源等非密码学手段降低损失。
- 密文 + 机器码同时泄露。这是派生方案的直接推论:知道这两样的人不需要联网、不需要服务端,本地就能解出内容。我们能做的是让机器码不轻易外流,而不是让这条链算不出来。
- 服务端校验被整体绕过。播放器如果被改造成"本地跳过 verify",文件层防护仍在,但"换机失效""到期失效""使用次数限制"这些依赖在线校验的能力会全部失效。
所以这套方案的准确表述是:它让「随手转卖文件」和「共享一枚激活码」这两件事变得不成立,让「篡改内容」变得可被发现,而不是"内容不可能被复制"。任何声称后者的方案,都在越过密码学能承诺的边界。
09可复核:五个问题判断一个加密工具
论证落到实用处,是一张能拿去问别人的清单。对方如果答不上前两个,后面不必再问。
| 问题 | 合格答案长什么样 | 可疑信号 |
|---|---|---|
| 1. 用的什么算法与模式 | 具体到 "AES-256-GCM" 这个粒度 | 「自研混淆算法」「军用级加密」 |
| 2. 密钥谁生成、存在哪 | 说得出派生输入,或说得出密钥由谁在什么条件下下发 | 「密钥写在文件里,但别人看不见」 |
| 3. 有没有认证标签 | 能明确回答"改一个字节会不会被发现" | 只讲"打不开""乱码",不讲完整性 |
| 4. 有没有设备绑定 | 换机后文件自动失效,需要重新授权 | 「同一个文件到处都能放」 |
| 5. 播放是否依赖在线校验 | 说得清过期、使用次数、断网策略各自怎么处理 | 含糊其辞,或"永久离线可用" |
这张表也是我们自己在内部改代码时的自检项:每一次改动容器格式、密钥来源或校验逻辑,都要能在表里给出新的明确答案,否则就是改坏了边界。
10结论
回到开头那句命题:防护强度的上限由密钥的可获得条件决定,算法只决定下限。四条论证分别是——公钥方案不增加防护,只增加失败点;对称加密必须带认证,否则篡改会被伪装成花屏;密钥来源比算法位数更重要,机器码派生把密钥与设备绑在一起,代价是机器码外流等于密钥外流;KDF 的选择由熵源决定,128 bit 设备标识不需要慢哈希,但换成人类口令就必须换。
这套实现从第一代的循环密钥异或走到今天的 AES-256-GCM,走了两年。中间那次弯路留下的教训很朴素:一个方案可被验证的前提,是它能用具体算法名、具体模式、具体密钥来源回答别人的问题。答不上来时,说明它自己也没想清楚。
本文依据
容器结构(.dat 52 字节头、SLF1 魔数、索引区与数据区分段)、密钥派生方式(机器码 SHA256 取前 32 字节)、GCM 参数(每文件独立 12 字节 nonce、16 字节标签)均取自产权保护加密工具与播放器包内的实际实现,不是纸面设计。
RSA-2048 在 PKCS#1 v1.5 填充下单次明文上限 245 字节,属算法定义,非本方案结论。位翻转攻击对 CTR 模式的适用性属公开的密码学事实,本文只做原理说明,不含可操作的破解步骤。