音视频-加密技术方案,前后端实现步骤,案例

2022-06-06 工具 阅读 2123 次

基于之前音视频-加密技术方案,总结了前端,后端,存储,三个角色实现的案例

案例技术栈

ffmpeg, sh, node, http-server, hls.js

我将一个视频加密过程和到播放器播放,总结以下几个步骤

  1. 切片,分片,分片加密
  2. 服务器key,中间层加密
  3. 前端播放器,扩展解密播放
  4. key 加密解密方案

1. 切片,分片,分片加密

  1. mkdir ffmpeg-to-m3u8
  2. cd ffmpeg-to-m3u8
  3. vi test.sh 建立脚本
  #!/bin/bash
  i=2
  while true
  do
      sleep 3
      tmpfile=`mktemp`
      openssl rand 16 > enc$i.key
      echo https://hjphhh.net/enc$i.key > $tmpfile
      echo enc$i.key >> $tmpfile
      echo `openssl rand -hex 16` >> $tmpfile
      mv $tmpfile enc.keyinfo
      let i++
  done
  1. sh ./test.sh 执行脚本
  2. 新建命令窗执行,分片机密
ffmpeg -y \
    -i test.mp4  \
    -hls_time 20 \
    -hls_flags periodic_rekey \
    -hls_key_info_file enc.keyinfo \
    -hls_playlist_type vod \
    -hls_segment_filename "file%d.ts" \
    playlist.m3u8
  1. 最终得到多key m3u8文件
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:20
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-KEY:METHOD=AES-128,URI="https://hjphhh.net/enc3.key",IV=0x73b9a60a640b21d0862c37d93070edf7
#EXTINF:20.000000,
file0.ts
#EXT-X-KEY:METHOD=AES-128,URI="https://hjphhh.net/enc4.key",IV=0x99d6d1274fdabbc7a980919b24f64f19
#EXTINF:20.000000,
file1.ts
#EXTINF:20.000000,
file2.ts
#EXT-X-KEY:METHOD=AES-128,URI="https://hjphhh.net/enc5.key",IV=0xf61b64780bc7ac9a8c952cfc774ac500
#EXTINF:20.000000,
file3.ts
#EXT-X-KEY:METHOD=AES-128,URI="https://hjphhh.net/enc6.key",IV=0x99136c251d08603e65f6ff99db0a1402
#EXTINF:20.000000,
file4.ts
#EXTINF:20.000000,
file5.ts
#EXT-X-KEY:METHOD=AES-128,URI="https://hjphhh.net/enc7.key",IV=0x83184dac99a749345288d02594d56b93
#EXTINF:20.000000,
file6.ts
#EXTINF:20.000000,
file7.ts
#EXT-X-KEY:METHOD=AES-128,URI="https://hjphhh.net/enc8.key",IV=0xb56153c847bdc36bb059b1c61c78b548
#EXTINF:20.000000,
file8.ts
#EXTINF:20.000000,
file9.ts
#EXT-X-KEY:METHOD=AES-128,URI="https://hjphhh.net/enc9.key",IV=0x164eb6fa500fff78459dd2d2742b4c04
#EXTINF:0.600000,
file10.ts
#EXT-X-ENDLIST

音视频-加密技术方案,前后端实现步骤,案例-黄继鹏博客

2. 服务器key,中间层加密

通过http-server在分片文件目录启动静态服务

http-server --cors

此时通过URL 访问m3u8文件,在iOS,safari浏览器也可直接播放

或者打开hls官方网页播放器,输入地址播放

http://videojs.github.io/videojs-contrib-hls/

音视频-加密技术方案,前后端实现步骤,案例-黄继鹏博客

可以看到请求分片ts,会不间断请求key解密接口。

以上案例没有做key接口加密处理。

其实加密原理也简单,key为一个url,播放器每次加载ts分片都会不间断请求这个key接口,接口的控制权限在服务端,服务端可以通过路径找到key文件,把数据进行加密给到客户端,客户端通过服务端自定义业务逻辑,进行key response机密,完成播放。

3. 前端播放器,扩展解密播放

hls播放器,针对自定义加密key,如何处理?

在key不做自定义加密情况下,播放器可以直接解析key进行播放。

如果key加密了,我们需要在拿到加密字符后,先不将结果给到播放器,需要先进行解密,在进行喂养。

hls.js源码找到了以下的片段

function loadsuccess(
  response: LoaderResponse,
  stats: LoaderStats,
  context: KeyLoaderContext
) {
  let frag = context.frag;
  if (!frag.decryptdata) {
    logger.error("after key load, decryptdata unset");
    return;
  }
  this.decryptkey = frag.decryptdata.key = new Uint8Array(
    response.data as ArrayBuffer
  );

  // detach fragment loader on load success
  frag.loader = undefined;
  delete this.loaders[frag.type];
  this.hls.trigger(Event.KEY_LOADED, { frag: frag });
}

那么如何修改这个 func 使得可以在解密呢?

我们在 hls 的配置项里面找到了这个 https://github.com/video-dev/hls.js/blob/master/docs/API.md#loader

loader
(default: standard XMLHttpRequest-based URL loader)

Override standard URL loader by a custom one. Use composition and wrap internal implementation which could be exported by Hls.DefaultConfig.loader. Could be useful for P2P or stubbing (testing).

Use this, if you want to overwrite both the fragment and the playlist loader.

Note: If fLoader or pLoader are used, they overwrite loader!

我们试着通过改写这个 loader 方法来完成我们的业务场景:

const configure = {
  loader() {
    const loader = new Hls.DefaultConfig.loader(configure);
    this.abort = () => loader.abort();
    this.destroy = () => loader.destroy();

    this.load = (context, config, callbacks) => {
      const { type } = context;
      const onSuccess = callbacks.onSuccess;
      callbacks.onSuccess = (response, stats, context1, networkDetails) => {
        // 标示key方法 因为请求中m3u8请求和ts请求都在这里
        if (type !== "manifest" && context1.url.includes("key?devId")) {
          response.data = _base64ToArrayBuffer(ab2str(response.data));
        }
        onSuccess(response, stats, context, networkDetails);
      };
      loader.load(context, config, callbacks);
    };
  }
};

保留了原来的各类方法,又将我们需要的处理函数注入到代码中,这样就解决了自定义注入key的问题。

4. key 加密解密方案

加密流程

转码加密后生成的m3u8文件带有“#EXT-X-KEY”标签,该标签包含了“METHOD”和“URI”属性,其中“URI”即为业务侧搭建的密钥管理服务的地址,示例如下所示。

#EXTM3U  
#EXT-X-VERSION:3  
#EXT-X-TARGETDURATION:6  
#EXT-X-MEDIA-SEQUENCE:0 
#EXT-X-KEY:METHOD=AES-128,URI="https://domain-sample/encrypt/get-key?asset_id=6aee80009c4ca6970f508d6334194794",IV=0x80a3ff24ccd788042ca7f2237e74c59d  
#EXTINF:5.000000,  6aee80009c4ca6970f508d6334194794_1_1920X1080_3000_0_0.ts  
#EXTINF:5.000000,  6aee80009c4ca6970f508d6334194794_1_1920X1080_3000_0_1.ts  
#EXT-X-ENDLIST

解密流程

  1. 终端用户登录播放器终端,业务侧会对终端用户进行身份校验,校验通过后,会为播放终端分配一个Token,并将带Token播放地址返回给播放器端。

    若转码加密后的HLS视频播放地址为:https://1280.cdn-vod.huaweicloud.com/input/test.m3u8,则播放器终端获取的播放地址为:https://1280.cdn-vod.huaweicloud.com/input/{token}/test.m3u8

  2. 播放器终端通过带Token的播放URL向CDN请求播放。由于Token是动态的,所以CDN收到请求后,会直接回源到点播服务。点播服务会将请求URL中的Token写入m3u8文件的的“URI”中。

    点播服务返回给CDN的m3u8文件中会携带播放终端的Token值,示例如下所示。

    #EXTM3U  
    #EXT-X-VERSION:3  
    #EXT-X-TARGETDURATION:6  
    #EXT-X-MEDIA-SEQUENCE:0 
    #EXT-X-KEY:METHOD=AES-128,URI="https://domain-sample/encrypt/get-key?asset_id=6aee80009c4ca6970f508d6334194794&token={token}",IV=0x80a3ff24ccd788042ca7f2237e74c59d  
    #EXTINF:5.000000,  6aee80009c4ca6970f508d6334194794_1_1920X1080_3000_0_0.ts  
    #EXTINF:5.000000,  6aee80009c4ca6970f508d6334194794_1_1920X1080_3000_0_1.ts  
    #EXT-X-ENDLIST
    
  3. 播放终端解析返回的m3u8文件,得到EXT-X-KEY标签中的“URI”内容,向“URI”请求密钥。

  4. 业务侧的密钥管理服务收到请求后,先验证Token的合法性,若Token合法,则通过调用点播服务的API查询密钥。

    密钥管理服务可以选择将查询到的密钥缓存在本地,当下次有其它播放终端请求时,可以直接返回,无需每次都向点播服务获取。

  5. 密钥管理服务将点播服务返回的密钥返回给播放终端。播放终端通过获取的密钥解密播放m3u8文件。

参考文献:

音视频demo地址:https://hls-js.netlify.app/demo/

How to Encrypt Video for HLS:http://hlsbook.net/how-to-encrypt-hls-video-with-ffmpeg/

hls播放器:https://videojs.github.io/videojs-contrib-hls

ffmpeg文档:https://ffmpeg.org/ffmpeg-formats.html

[总结]FFMPEG视音频编解码零基础学习方法:https://blog.csdn.net/leixiaohua1020/article/details/15811977

ffmpeg参数中文详细解释:https://blog.csdn.net/leixiaohua1020/article/details/12751349

使用 FFmpeg 生成 ts 切片并使用 AES-128 加密:https://rockycoder.cn/ffmpeg/2018/10/26/Generate-encrypted-video.html

腾讯课堂点播上云客户端实践总结:https://www.jeesell.com/details/42.html

使用 FFmpeg 生成 ts 切片并使用 AES-128 加密:https://smtxi.com/14.html

hls以及普通加密模式:https://zhuanlan.zhihu.com/p/102125509

0条评论
...