100u 有偿请前端老哥实现解密播放 m3u8 文件

8 天前
 newbie111

1.通过以下命令对影片的明文 m3u8 和 ts 文件进行加密( aes-ecb ):

openssl enc -aes-128-ecb -in decrypt/index.m3u8 -out encrypt/index.m3u8 -K $(echo -n "你自己任意指定的 16 位加密 key" | xxd -p)

openssl enc -aes-128-ecb -in decrypt/001.ts -out encrypt/001.ts -K $(echo -n "你自己任意指定的 16 位加密 key" | xxd -p)

2.前端页面请求到 m3u8 播放链接时解密文件并播放。

这里我上传了一个不到 10 秒的未加密素材供测试,当然你也可以自己找个 mp4 文件切片成 m3u8 然后本地测试解密播放,先提前谢过各位前端老哥了,一旦成功解密播放,立即支付 100u 表达谢意。我的理解是 hls.js 或 crypto.js 之类的库来解密,但我不是专业前端所以改起来有困难。。。 https://drive.google.com/drive/folders/1QPD_E6C34tND0hICVrWIemxjRL-RLnad?usp=sharing

5216 次点击
所在节点    React
57 条回复
ppddtt
8 天前
这样做没有意义,本质上还是本地解密,客户端包含密钥信息
dyllen
8 天前
@newbie111 你这话好多问题,前端项目的.env 文件并不在前端,那是在服务器保存的,并不会暴露。人家说的前端指浏览器端,不是你这前端项目。aes 加密,浏览器播放端要解密,必定需要密钥,别人就能拿到。
tool2dx
8 天前
还不如学 youtube ,直接把视频接口从 get 都改成 post 。

个人感觉,把后端鉴权做好就够了,保证走你自己加密的 js 那一套 web 流程。
EridanusSora
8 天前
视频加密的正确方法:上 DRM ,比如 widevine
easydou
8 天前
加密 m3u8 的话,也可以用国内的一些厂商产品,比如保利威,阿里云等。如果自己写的话,最好打包成 wasm ,这样破解难度高,解密速度也比直接 js 解密要快
fly9i
8 天前
一般常规做法是在 m3u8 不加密,ts 文件加密,每个 ts 文件密钥可以不一样,加密一般是用 aes-128-cbc 。
m3u8 中配置一条
#EXT-X-KEY:METHOD=AES-128,URI="https://example.com/path/to/key",IV=0xabcdef0123456789abcdef0123456789

类似这样的数据,hls.js 每次请求 ts 前会先先请求密钥,ts 解密也是库里自动了。
thinkingbullet
8 天前
@yuzo555 ffmpeg -i video.mp4 -codec:v libx264 -codec:a aac -strict -2 -f hls -hls_key_info_file key_info.txt -hls_segment_type mpegts -hls_encryption_algorithm AES-128 -hls_key_url http://example.com/path/to/key playlist.m3u8
gpt3.5 的答案是这个,不知道谁真谁假
abusizhishen
8 天前
用 ffmpeg 加密之后,得到 m3u8 文件,把其中的解密 key 加密,前端播放前解密,这样对方拿到 m3u8 也无法播放,不过问题是解密是在前端,对方研究代码也能找到破解的方法,代价是花费更多的时间
puzzle9
8 天前
m3u8
要不 你自己重新实现下解密流程 像那种视频网站一样
增加下破解难度而已
joewang1988
8 天前
做完了,怎么交付?
joewang1988
8 天前
收款账号信息

Address :TD8j8Z76JP4t9PZPdtspZVbgwnFt2FDAUf
Token:USDT
Network:Tron


原理

使用 hlsjs 的 custom loader 在请求到 m3u8 和 ts 文件时,分别进行相对应的解密操作。

以下是完整代码




<html>

<head>
<title>Hls.js demo - basic usage</title>
</head>

<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hls.js/1.5.15/hls.min.js"></script>
<script src="./crypto-js.min.js"></script>

<center>
<h1>Hls.js demo - basic usage</h1>
<video height="600" id="video" controls></video>
</center>

<script>





async function process(playlist) {

const key = '1111111111111111';

const keyBytes = CryptoJS.enc.Utf8.parse(key);

console.log(playlist);

const byteArray = new Uint8Array(playlist);
const wordArray = CryptoJS.lib.WordArray.create(byteArray);


// Decrypt the data
const decrypted = CryptoJS.AES.decrypt(
{ ciphertext: wordArray },
keyBytes,
{ mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }
);

console.log(decrypted)
// Convert decrypted data to text
const decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
console.log(decryptedText);
return decryptedText;
}



// 转换 decrypted 为 ArrayBuffer
function wordArrayToArrayBuffer(wordArray) {
// 计算需要的长度
const length = wordArray.sigBytes;
const words = wordArray.words;
const bytes = new Uint8Array(length);

// 将 wordArray 的每个 word 转换为 byte
for (let i = 0; i < length; i++) {
bytes[i] = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xFF;
}

return bytes.buffer;
}

async function processData(data) {
console.log(data);

const key = '1111111111111111';

const keyBytes = CryptoJS.enc.Utf8.parse(key);

console.log(data);

const byteArray = new Uint8Array(data);
const wordArray = CryptoJS.lib.WordArray.create(byteArray);


// Decrypt the data
const decrypted = CryptoJS.AES.decrypt(
{ ciphertext: wordArray },
keyBytes,
{ mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }
);

console.log(decrypted)

const arrayBuffer = wordArrayToArrayBuffer(decrypted);

return arrayBuffer;
}

class CustomLoader extends Hls.DefaultConfig.loader {
constructor(config) {
super(config);
var load = this.load.bind(this);
//var xhrSetup = this.xhrSetup.bind(this);



this.load = function (context, config, callbacks) {
context.responseType = 'arraybuffer';


if (context.type == 'manifest') {
var onSuccess = callbacks.onSuccess;
callbacks.onSuccess = function (response, stats, context, networkDetails) {
console.log(networkDetails)
process(response.data)
.then(data => {
response.data = data;
onSuccess(response, stats, context);
})
.catch(err => {
console.error(err);
})

};
} else {
const onSuccess = callbacks.onSuccess;
callbacks.onSuccess = function (response, stats, context) {


processData(response.data)
.then(data => {
response.data = data;
onSuccess(response, stats, context);
})
.catch(err => {
console.error(err);
})


};
}
load(context, config, callbacks);
};
}
}

// Create the Hls instance with the custom fetch loader
const myHls = new Hls({
//debug: true,
loader: CustomLoader,
xhrSetup: function (xhr, url) {


xhr.open('GET', url, true);
xhr.setRequestHeader('id', 1)
xhr.setRequestHeader('token', 456)
xhr.responseType = '111'; // do send cookies
console.log("XXXXXXX")
}

});

const video = document.getElementById('video');
myHls.loadSource('./index.e.m3u8');
myHls.attachMedia(video);
myHls.on(Hls.Events.MANIFEST_PARSED, function () {
video.play();
});

myHls.on(Hls.Events.ERROR, function (event, data) {
if (data.fatal) {
console.error('HLS.js Error:', data);
}
});

</script>
</body>

</html>
rekulas
8 天前
还有个骚操作办法,先视频预处理对 rgb 通道进行异或,前端播放时根据 key 还原,速度非常快对性能影响很小,虽然没加密但是对方没 key 播放出来就是花屏
Oldletter
8 天前
BaiLinfeng
8 天前
这是想做啥黑科技
Ipsum
8 天前
@BaiLinfeng 我感觉是想放毛片在国内 cdn 上躲避审查。
BaiLinfeng
8 天前
@Ipsum 还可以这样玩的吗?有 demo 吗,我看看,那么骚的操作
azhangbing
8 天前
31 楼正确 你只需要重写 class CustomFragmentLoader extends Hls.DefaultConfig.loader 的 load 方法就行了

load(context, config, callbacks) {
const onSuccessOriginal = callbacks.onSuccess;

callbacks.onSuccess = (response, stats, context) => {
if (context.frag && context.frag.url && context.frag.url.includes('.ts')) {
const encryptedData = new Uint8Array(response.data);
const filenameBase = this.extractFilename(context.frag.url);



const decryptedData = xorDecrypt(encryptedData, this.keyBytes);
response.data = decryptedData.buffer;


}
onSuccessOriginal(response, stats, context);
};

super.load(context, config, callbacks);
}
Yaavi
8 天前
上午写完的,但联系不上,还能拿到 100u 么 [狗头]
Demo:[https://decipher-m3u8.yaavi.me/]( https://decipher-m3u8.yaavi.me/)
aycclm
8 天前
你们真的是卷死了 [手动狗头]
100u 还真给做了
jjl19960910
8 天前
@Ipsum #35 hh 笑死

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1071836

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX