How to decrypt AES-128 encrypted m3u8 video files?

48,074

Solution 1

In order to decrypt encrypted video stream you need encryption key. This key is not part of the stream. It should be obtained separately.

EXT-X-FAXS-CM header contains DRM meta-data and not the key.

This is excert from Adobe Media Server developer guide: The Adobe Access Server protected variant playlist also needs to include the #EXT-X-FAXS-CM tag. The value of #EXT-X-FAXS-CM tag in variant playlist is the relative URI referring to the DRM metadata of one of the individual streams.At the client, the #EXT-X-FAXS-CM tag in variant playlist will be used to create the DRM session. The same DRM session will be used for all encrypted M3U8 files inside the variant playlist.

Full guide can be found here: http://help.adobe.com/en_US/adobemediaserver/devguide/WS5262178513756206-4b6aabd1378392bb59-7fe8.html

There is also mention that faxs://faxs.adobe.com URI is for local key serving. So key obtained locally from a device.

Solution 2

This might be a bit of a hack, but given a URL to an .m3u8 file, it will download and decrypt the files that make up the stream:

#!/usr/bin/env bash
curl "$1" -s | awk 'BEGIN {c=0} $0 ~ "EXT-X-KEY" {urlpos=index($0,"URI=")+5; ivpos=index($0,"IV="); keyurl=substr($0, urlpos, ivpos-urlpos-2); iv=substr($0, ivpos+5); print "key=`curl -s '\''"keyurl"'\'' | hexdump -C | head -1 | sed \"s/00000000//;s/|.*//;s/ //g\"`"; print "iv="iv} $0 !~ "-KEY" && $0 ~ "http" {printf("curl -s '\''"$0"'\'' | openssl aes-128-cbc -K $key -iv $iv -d >seg%05i.ts\n", c++)}' | bash

This script generates a second script that extracts keys and initialization vectors and uses them to decrypt while downloading. It needs curl, awk, hexdump, sed, and openssl to run. It'll probably choke on an unencrypted stream, or on a stream that uses something other than AES-128 (is any other encryption supported?).

You'll get a bunch of files: seg00000.ts, seg00001.ts, etc. Use tsMuxeR (https://www.videohelp.com/software/tsMuxeR) to merge these into a single file (simple concatenation didn't work for me...it's what I tried first):

(echo "MUXOPT --no-pcr-on-video-pid --new-audio-pes --vbr  --vbv-len=500"; (echo -n "V_MPEG4/ISO/AVC, "; for i in seg*.ts; do echo -n "\"$i\"+"; done; echo ", fps=30, insertSEI, contSPS, track=258") | sed "s/+,/,/"; (echo -n "A_AAC, "; for i in seg*.ts; do echo -n "\"$i\"+"; done; echo ", track=257") | sed "s/+,/,/") >video.meta
tsMuxeR video.meta video.ts

(Track IDs and framerate may need adjustment...get the values to use by passing one of the downloaded files to tsMuxeR.)

Then use ffmpeg to remux to something a bit more widely understood:

ffmpeg -i video.ts -vcodec copy -acodec copy video.m4v

Solution 3

In many cases, VLC will happily convert an .m3u8 video to an unencrypted .ts or .mp4. In the VLC graphical interface, go to Media > Convert/Save.

Solution 4

While some of the bash scripts in the existing answers get you part (or even all) of the way, depending which site you're trying to download from, you might hit other obstacles (different auth method, custom license server mount, etc.)

I've found streamlink to be the most robust solution for this, which also lets you stream directly (rather than download), if that's what you're after, and it has all the site-specific work already done for you for a long list of sites (see plugins section, but keep in mind it's under active development and the latest release was in June, so for some of the newer ones you'll have to git clone and install from source).

Share:
48,074
dabiboo
Author by

dabiboo

Updated on July 15, 2022

Comments

  • dabiboo
    dabiboo almost 2 years

    I trying to decrypt AES-128 encrypted m3u8 video files such as this one :

    the m3u8 file :

    #EXTM3U
    #EXT-X-MEDIA-SEQUENCE:0
    #EXT-X-ALLOW-CACHE:NO
    #EXT-X-VERSION:2
    #EXT-X-FAXS-CM:MII6lAYJKoZIhvcNAQcCoII6hTCCOoECAQExCzAJBgUrDgMCGgUAM... very long key...
    #EXT-X-KEY:METHOD=AES-128,URI="faxs://faxs.adobe.com",IV=0X99b74007b6254e4bd1c6e03631cad15b
    #EXT-X-TARGETDURATION:8
    #EXTINF:8,
    video.mp4Frag1Num0.ts
    #EXTINF:8,
    video.mp4Frag1Num1.ts
    ...
    

    I've tried with openssl :

    openssl aes-128-cbc -d -kfile key.txt -iv 99b74007b6254e4bd1c6e03631cad15b -nosalt -in video_enc.ts -out video_dec.ts
    

    key.txt contains the very long key -->

    bad decrypt
    1074529488:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:539:
    

    What am-I doing wrong ?

  • dabiboo
    dabiboo about 11 years
    ffmpeg can't handle this kind of m3u8. I think I can read it and find the useful input for openssl, the characters following the "#EXT-X-FAXS-CM:" are a base64 p7b encoded file. Once I base64 decode it and save it into a .p7b file, I can open it with a Ms Windows viewer. I know need to know open to retreive the key from this p7b file.
  • dabiboo
    dabiboo almost 11 years
    It seems way harder than I thought, thanks for your explanation. I will now try to find the key.
  • wget
    wget about 7 years
    To be able to print it properly on non Windows machine, simply convert it to CER (cf. this answer stackoverflow.com/a/22259331/3514658)
  • Simon Rozman
    Simon Rozman about 6 years
    I had to modify your script because URLs in my case contained % signs from URI encoding. This is what worked for me: curl "$1" -s | awk 'BEGIN {c=0} $0 ~ "EXT-X-KEY" {urlpos=index($0,"URI=")+5; ivpos=index($0,"IV="); keyurl=substr($0, urlpos, ivpos-urlpos-2); iv=substr($0, ivpos+5); print "key=`curl -s '\''"keyurl"'\'' | hexdump -C | head -1 | sed \"s/00000000//;s/|.*//;s/ //g\"`"; print "iv="iv} $0 !~ "-KEY" && $0 ~ "http" {printf("curl -s '\''%s'\'' | openssl aes-128-cbc -K $key -iv $iv -d >seg%05i.ts\n", $0, c++)}' | bash
  • John Bale
    John Bale about 6 years
    This site: support.jwplayer.com/customer/portal/articles/1430261 has an encrypted m3u8 url: playertest.longtailvideo.com/adaptive/oceans_aes/… I copied the script into a text file and named it script.sh, in terminal I then run bash script.sh 'playertest.longtailvideo.com/adaptive/oceans_aes/…' but nothing happens, am I doing something wrong?
  • Simon Rozman
    Simon Rozman about 6 years
    @John, the differences with this m3u8 file descriptor are: 1. Encryption does not use an initialization vector, so this script fails while searching for "IV=" string. 2. The URLs in the m3u8 file are relative, again making the script to fail. For this particular case, the script needs amends.
  • Simon Rozman
    Simon Rozman about 6 years
    Try this: BASE=https://playertest.longtailvideo.com/adaptive/oceans_ae‌​s/; M3U8=oceans_aes-audio=65000-video=2042000.m3u8; curl "${BASE}${M3U8}" -s | awk "BEGIN {c=0} \$0 ~ \"EXT-X-KEY\" {urlpos=index(\$0,\"URI=\")+5; keyurl=substr(\$0, urlpos, length(\$0)-urlpos); print \"key=`curl \\\"${BASE}\"keyurl\"\\\" -s | hexdump -C | head -1 | sed \\\"s/00000000//;s/|.*//;s/ //g\\\"`\"} \$0 !~ \"^#\" {printf(\"curl \\\"${BASE}%s\\\" -s | openssl aes-128-cbc -K \$key -iv 00000000000000000000000000000000 -d >seg%05i.ts\\n\", \$0, c++)}" | bash
  • John Bale
    John Bale about 6 years
    Thanks for the response, I was able to download the video last week with hlsdl, github.com/selsta/hlsdl
  • pensnarik
    pensnarik about 4 years
    Thank you for your script. It should work fine but there's a problem. I'm trying to use it to download a stream from boomstream.com but the key I receive from URL is always different. What could it mean?