← 资讯与洞察

密钥是算出来的:媒体传播加密的三层与三道关

课程视频发出去之后,最常见的三件事是文件被转卖、激活码被共享、屏幕被翻录。这篇不讲"加密多安全",而是把我们产权保护系统里的真实实现摊开:一个叫 SLF1 的容器格式、由机器码算出来的密钥、每次播放要过的六道闸门,以及这套方案从原理上就防不住的两件事。

加密不是一个开关,而是一串先后关系:文件先变成打不开的东西,打不开的东西先要过设备这一关,设备这一关要靠服务端点头。这条链上每一环都能被绕过,问题只在于绕过它需要多少成本。下面按代码里的真实顺序讲。

01一条视频只有三种被搬走的方式

讨论视频防盗,最容易犯的错是把三件不同的事混成一件。它们挡的人不一样,能被破的方式也不一样。

层做什么挡住谁怎么被破
文件层 把视频装进一个只有专用播放器认识的加密容器 拿到文件直接双击、拖进网盘、当附件转发的人 拿到密钥,或拿到会算密钥的播放器
设备层 激活码只认一台机器,文件拷走也没用 把文件拷给别人、把激活码转手的人 整台电脑一起搬走,或远程控制别人的桌面
在线层 每次(或每七天)向服务端确认一次还能不能播 已被吊销、已过期、被判定异常使用的授权 断网使用缓存、伪造校验结果

行业里被高估的总是第一层。AES 密文当然解不开,但加解密的两端都在同一台机器上——播放器能解,这台机器就一定能解。所以真正决定方案强弱的不是算法,而是密钥在什么条件下才被算出来、算出来之后谁允许它被使用。

02文件层:一个 52 字节的文件头

我们的加密工具不产出 mp4,产出的是一个后缀为 .dat 的容器,内部格式代号 SLF1。整个容器由三段组成:一个固定 52 字节的头、一段加密的索引、一段接一段的文件数据。

区段内容为什么这样放
头 0–4 魔数 SLF1 一眼认出这是新版容器,不是随便改后缀的 mp4
头 8–40 32 字节 HMAC-SHA256 打开文件的第一关:算不出这个值,说明不是这台机器
头 40–52 文件数、索引长度、数据区长度 播放器不开整包也能定位到要播的那一段
索引区 AES-GCM 加密的路径表 连"里面有几节课、每节叫什么"都不暴露
数据区 每个文件一段 nonce + 密文 + 认证标签 逐文件独立加密,播第 3 节不用解开第 1 节

几个具体的工程取舍值得说清:

SLF1 容器的三段结构 整包只有一个文件,播放器却只解它要播的那一节 文件头 · 52 B 索引区(同样加密) 数据区(逐文件独立加密) "SLF1" 魔数 HMAC-SHA256 32 B 文件数 / 索引长 / 数据长 绑定机器码,第一道关 AES-GCM 密文 解密后是路径表 路径 · 明文大小 · 偏移 不解开就不知道有几节课 nonce 12B 密文 + tag 16B 第 2 节 独立密钥流 第 3 节 当前要播的 第 N 节 不解不开 每节各自 nonce,改一个字节就认证失败 密钥 = SHA256(机器码) 的前 32 字节 密钥不进文件,由本机机器码现算 所以换一台机器,算出来的密钥就不同 代价:算得出不代表播得动 播放器:先解索引,再按偏移取一节 1. 用机器码算密钥 → 校验头里的 HMAC 2. 解索引 → 得到第 3 节的明文大小与偏移 3. 只读那一段 → 认证解密 → 交给播放内核 注:整包的大小可以做到几百 MB,但播放时进内存的只有当前这一节的明文。 这也是它能在一台 8G 内存的办公机上播完 20 节课的原因。
图 1 SLF1 的三段结构。头里那 32 字节不是"校验码",而是用密钥对固定字符串做的 HMAC——机器码不对,第一步就过不去。

03密钥是算出来的,不是发下来的

大多数加密方案的密钥由服务端生成、在授权通过后下发。我们没有这么做:密钥是客户端自己算的——把本机机器码做一次 SHA256,取前 32 字节,就是 AES-256 的密钥。

这么设计有两个好处。第一,服务端从头到尾不持有密钥,也就不存在"密钥库被拖库"这个风险;第二,链路上没有"下发密钥"这个环节,抓包抓不到密钥。但它同时也带来一个必须承认的代价:只要有人知道目标机器码,他就能自己算出这把密钥。加密强度在这里不解决任何问题,因为问题的答案根本不在密码学里,而在"机器码是秘密吗"。

这就是为什么整条链路的重心不在加密那一层,而在后面两道:机器码只属于那台机器,而服务端决定要不要放行。用一句更直白的话说——文件层负责让门外的人打不开,授权层负责让门里的人也不一定打得开。

如果只做"文件加密",把密钥直接写进文件头,那它就退化成一个改了后缀的压缩包:任何人拿到文件都能顺手解开。真正的分水岭是:密钥是不是由"某个只属于这台机器的信息"派生出来的。

这也是我们放弃"随机密钥 + 服务端下发"路线的原因

04设备层:机器码是一个哈希,不是一个串号

客户在自己电脑上跑一次机器码工具,得到 32 位十六进制。这个值的生成过程只有两步:

这套做法有两个真实代价,我们选择把它写在明面上:

机器码这一层的能力与代价

  • 换电脑、拷贝文件、重装播放器,都不影响"这个激活码只认原来那台机器"。
  • 磁盘串号只以哈希形式出现在授权记录里,服务端数据库被看到也不等于客户硬件信息泄露。
  • 换硬盘就等于换机器。机器码只看磁盘序列号,不看 CPU、主板、网卡,所以硬盘一换,原激活码立刻失效——这是客服最常见的一类问题。
  • 重装系统在多数机器上不影响(物理磁盘序列号不变),但如果当初取的是 C 盘卷序列号这条回退路径,重装格式化就会变。
  • 机器码本身是可以被"伪造环境"绕过的:虚拟机克隆、磁盘序列号可改的工具都真实存在。它挡的是普通用户,不是专门搞这个的人。

如果只从"防破解"的角度看,这几条都是缺点。但它们换来的是另一个更重要的东西:客户不需要理解加密。他只要打开一个工具、把一串字符发给内容方,剩下的事都在后台完成。任何需要客户"手动保护密钥"的方案,实际使用中都会失败。

05在线层:一次播放要过六道闸门

播放器每次决定"播不播"之前,会向服务端发一次请求,只带三个字段:激活码、机器码、播放器类型。服务端不查文件、不查密码,只查这条授权记录还能不能用。判定是一串有先后顺序的问题:

顺序判定条件不通过时返回为什么放在这一位
1激活码在库里存在吗404「激活码无效或不存在」先排除猜码,避免后面的信息被试探出来
2授权状态是 active 吗「激活码已停用」管理端吊销后,这条必须先生效
3机器码与记录一致吗「激活码与当前设备不匹配」设备不符时,讨论到期与否没有意义
4到期时间还在将来吗「激活码已过期」时间比较以服务端为准,不看本机时钟
5限量授权的次数还没用完吗「激活码使用次数已用完(单次授权)」次数是"用了就扣",只能放在放行前
6授权类型匹配这个播放器吗「激活码不适用于该播放器」视频 / SWF / 加密工具三类是分开授权的

六道全过,返回的不只是"可以播",还带回到期时间、剩余次数这些字段,让播放器能提前提示"还剩 3 天到期",而不是等到某天打开突然黑屏。第 5 道要注意一个细节:限量授权在放行前就把次数扣掉了,不是"播完再扣"。这样即使客户端在播放中途被强杀,次数也已经用掉,不会出现"开一百次窗口却不扣次数"的口子。

还有一个我们踩过的坑:接口在"参数缺失"和"激活码不存在"这两种情况下,返回的HTTP 语义并不一样。前者是参数校验失败,后者是业务上明确"没有这张卡"。客户端必须两种都能正确翻译成中文提示——只处理一种,用户就会看到"服务器异常",然后打客服电话说"你们的服务器坏了"。

一次校验要按顺序过六道闸门 任一条不过就立刻短路返回,不会继续往下判——顺序本身就是信息保护 01 卡存在吗 查不到 → 404 02 状态 active 激活码已停用 03 机器码一致 设备不匹配 04 未到期 激活码已过期 05 次数未用尽 次数已用完 06 类型匹配 不适用播放器 任一条不过 → 直接拒绝 返回具体原因,不是笼统的"校验失败" 播放器把这段文案原样展示给用户 六道全过 → 放行 带回到期时间、剩余次数 可提前提示"还剩 3 天" 这一层挡的不是"文件被拷走",而是"这台设备还能不能继续看"。 它也是唯一能让"昨天卖出去的卡,今天作废"生效的一层。 代价:它依赖网络,于是就有了下一节的七天缓存。
图 2 六道闸门的短路顺序。把"卡存不存在"放到第一位,是为了让猜码的人得不到任何有用信息。

06断网也要能播:七天窗口与"单次卡不缓存"

在线校验有一个躲不开的副作用:客户的电脑可能没有网络,或者授权服务器在重启。如果每次播放都必须联网成功,那这套系统在客户的第一次断网时就会变成"废品"。所以播放器里有一套本地缓存,规则写得很死:

授权形态是否会写本地缓存结果
年卡 / 终身卡(不限次) 会 校验成功后缓存 7 天,这 7 天内断网也能播;超过 7 天强制重新联网
单次卡 / 限量卡 不会 每次打开都必须联网校验,用一次扣一次,不给自己留离线空间
已被吊销 / 已过期 不写 下一次联网就会被拦;最长滞后 7 天(见下文)

缓存文件放在用户配置目录下,播放器与加密工具各用各的文件,互不覆盖——否则"用加密工具校验过一次",就会让播放器的授权类型也被改写,出现"明明买的是视频卡,却打不开视频"这种诡异故障。缓存命中的条件有五个,缺一不可:同一个激活码、同一台机器、同一种播放器类型、授权还没到期、缓存写入时间在 7 天内。

七天这个数字是权衡出来的,不是拍脑袋。缓存越久,客户断网体验越好,但吊销生效越慢——你今天把一张卡作废,对方最晚还能再播七天。反过来,缓存太短,客户每次出差都要重新联网。我们选择七天,是因为课程场景里"一周内至少连一次网"是合理预期;换成考试系统或按次付费,这个数字必须改成 0。

还有一个很小的细节,但它是客服电话的主要来源之一:激活码在粘贴时经常带空格或换行。所以客户端在校验前会把所有空白字符剔掉再比对——不是"容错设计",而是真实场景:客户是从微信聊天框里复制那串码的。

07播放器为什么有 125 MB

客户第一次看到安装包体积,通常会问一句"怎么这么大"。这不是打包没优化,而是因为它自带了一个完整的播放内核。

程序体积大头在哪
视频播放器131,350,528 B
约 125.3 MB
内嵌 libmpv 解码内核 120,326,144 B(约 114.7 MB)
SWF 播放器37,547,008 B
约 35.8 MB
Ruffle 的两份 WebAssembly 运行时,各约 14.3 MB
加密工具8,798,208 B
约 8.4 MB
纯 Go 逻辑,没有解码器
机器码读取工具8,636,928 B
约 8.2 MB
同上,图形界面占了大部分

为什么不用系统自带的播放器、或者网页播放器?两个原因都是真实约束:

代价是安装包大、首次启动慢一点:内核 DLL 第一次运行时会从 exe 里释放到临时目录,之后再启动就直接复用,不再重复写盘。这一步用"先写临时文件再原子改名"的方式做,避免同时开两个播放器把 DLL 写坏。

08这套方案防不住的三件事

任何做内容保护的人都会被问到同一个问题:"能不能做到绝对防住?"答案是不能,而且原因很具体。下面三条不是理论推演,是我们自己代码里就存在的路径。

一、明文会短暂落在磁盘上

播放器解出某一节的内容后,要交给播放内核。内核接受的是文件路径,所以明文必须先写成一个临时文件,播完再删掉。这意味着:在播放的那段时间里,一节视频的完整明文就躺在系统的临时目录里,文件名是一串随机串。正常关闭播放器会立刻清除,启动时也会清扫上次崩溃留下的残留——但如果进程被强杀、或者有人守在那个目录里复制,明文就会被拿走。

想把这一段补上,只有两条路:改成内存流加载(需要改造播放内核的加载方式,代价是兼容性下降),或者用更底层的文件系统过滤驱动(代价是装驱动、要签名、和杀毒软件打架)。我们选的是"播完就删 + 启动清扫",而不是"假装不存在这个窗口"。这一点必须在售前讲清楚:它防的是外部分发,不是本机取证。

二、屏幕录制拦不住

播放器里确实有键盘钩子,但它的用途是"全屏时按 Esc 退出全屏",不是拦截图键。系统级的 PrintScreen、Win+Shift+S、第三方录屏软件、外接采集卡、手机对着屏幕拍——这些都不经过播放器的进程,技术上无法从应用层阻断。

行业里对这个问题的标准答案不是"加更多钩子",而是水印与追责:在画面上叠一层跟着播放器走的用户标识(机器码后四位、手机号尾号),让流出的画面本身带出处。这件事我们目前没有做进播放器,是明确的待补项,不是已具备的能力。

三、整台机器搬走,等于没换机器

设备绑定认的是"这台电脑",不是"这个人"。所以两种情形会绕过它:把整机(或者整机镜像)搬到别处继续用;通过远程桌面让别人在自己的机器码上播。前者等于物理层面的拷贝,后者在授权系统眼里完全是合法设备。

另外,前面说过密钥由机器码派生。这条链有一个直接推论:如果攻击者同时拿到密文文件和目标机器的机器码,他不需要联网、不需要服务端,就能在本地把内容解出来。我们能做的是让机器码不轻易外流(授权记录里只存哈希),以及让"播得动"这件事依赖服务端——但拦不住一个已经拿到这两样东西的人。

把能力边界一次说完

  • 能做到:文件离开专用播放器打不开;激活码换机器不可用;过期/吊销/次数用尽能被拦;到期与剩余次数可提前提示。
  • 能做到:内容方不用懂加密,拿到机器码就能出加密包;学员不用装环境,双击就能看。
  • 做不到:阻止本机在播放期间取走明文;阻止屏幕录制与拍摄;阻止整机搬运与远程桌面;阻止已掌握机器码的人离线解密。
  • 做不到:实时吊销。最坏情况有一周窗口(缓存机制决定)。

09我们走过的弯路:从异或到 AES-GCM

这套工具的容器头里有一个魔数 SLF1,它不是装饰——它标记的是"这一版换了加密算法"。第一代版本用的是循环密钥异或:把密钥按字节循环,跟数据逐字节 XOR。文件看起来是一片乱码,双击打不开,表面上"加密了"。但它在密码学上几乎等于没加密:

版本算法真实强度
第一代 循环密钥异或 已知明文即可还原密钥;视频这种高度可预测的数据,统计特征原样保留。属于"看起来乱码",不属于加密。
当前版 AES-256-GCM 每个文件独立 nonce,密文带认证标签。改一个字节就解不开,也无法通过已知片段反推密钥。

为什么第一代会用异或?因为它简单、快、不用引入加密库,而且在"防的是随手转发"的场景里,它确实骗过了第一波人。这是很多小工具的真实起点:先解决"双击打不开",再解决"专业工具打不开"。我们把旧算法保留在代码里,只用于读历史文件——已经发出去的旧包不能因为升级而突然打不开,这在客户那里是不可接受的。

这条弯路值得写出来,因为它解释了一个判断标准:问一个卖加密工具的人"你用的什么算法",如果对方答不上来,或者答"我们自己研发的混淆算法",那大概率还停在第一代。真正可验证的答案只有具体算法名、具体模式、具体密钥来源。

10什么情况下值得上这套东西

先把场景分清楚,比先看价格有用。下面五种情况,判断标准不是"内容值多少钱",而是"内容以什么形式离开你的手"。

需要加密的几种情形

  • 课程以文件形式交付(U 盘、网盘链接、微信发送),学员可以把文件转给别人——这是最典型的适用场景。
  • 内容单价较高,一份被转卖就抵掉好几个新客户。
  • 需要"到期"这个能力:按年收费、按次收费、试用三天,都需要授权能在服务端被改。
  • 视频只在平台内播放(公众号、视频号、第三方课堂)。这些平台自带观看权限,再加一层文件加密没有意义。
  • 学员群体技术能力强、并且有明确的破解动机。加密能把成本抬高,但抬不到"拦得住"的程度。

价格按激活码计,一个激活码对应一条授权记录(一台设备)。官网现在挂的是这五档:

档位价格授权期激活码适合谁
单次加密授权卡¥498365 天1 个一次性使用,激活码用一次即作废;适合按次交付
基础版(年授权)¥998365 天100 个小班课、几十人规模的固定学员
专业版(年授权)¥2,388365 天不限(支持 3 台)持续招生的课程方,学员增长不用再补卡
一次性加密方案¥3,6803,650 天不限一次买断、长期做内容的人
OEM 定制¥9,800按需定制按需定制要换成自己品牌、走自己授权流程的机构
官网产权保护页实拍:三条痛点与三层防护
图 3 官网产权保护页实拍(1440 宽,2026-11-11)。上面三张是客户的原话,下面三层是这套系统的对外说法,和这篇里的工程实现是同一套东西:外层说人话,里层才是代码。

这些数字由服务端统一下发,页面上展示的和下单时计价的是同一份数据——不会出现"页面写 998、付款时变成别的数"的情况。价格与授权细则以产权保护页的实时展示为准,这篇只做工程说明。

想先确认自己的场景适不适合

产权保护页有机器码工具、加密工具的下载入口,以及三档能力的对照说明。拿你自己的机器跑一次机器码,再看一眼加密后的文件长什么样,比读十篇介绍更直接。

查看产权保护方案

本文事实来源

客户端实现:产权保护客户端工程(版本 2.0.1,2026-09-14)中的 crypto、machinecode、license、packer、mpvplayer 五个内部包,以及播放器、SWF 播放器、加密工具、机器码读取工具四个命令行程序。

容器格式:.dat 文件 52 字节头结构、索引区与数据区分段布局、支持的八种输入格式,均取自 packer 包源码。

授权判定:服务端授权模块的六条判定顺序与返回文案;五档价格取自授权模块的套餐定义(CRYBT_PLANS)。

体积数据:线上安装包目录实测字节数(播放器 131,350,528 B、SWF 播放器 37,547,008 B、加密工具 8,798,208 B、机器码读取工具 8,636,928 B);内嵌内核 libmpv-2.dll 为 120,326,144 B。

说明:本篇不包含播放器实机截图。文中两个示意图按源码结构绘制,不是产品界面。

讨论

加载中…

匿名即可发言,昵称留空会显示为“匿名访客”。评论按楼层顺序排列,每条都可以回复;站长会把有结论的回复标为「已采纳」。不支持链接与推广内容。在微信里打开本文可选用微信身份,显示你的头像与昵称。

0 / 400 字 · 每 IP 每小时 4 条

正在读取评论…