How to decrypt AES-128 encrypted m3u8 video files?
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).
dabiboo
Updated on July 15, 2022Comments
-
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 about 11 yearsffmpeg 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 almost 11 yearsIt seems way harder than I thought, thanks for your explanation. I will now try to find the key.
-
wget about 7 yearsTo 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 about 6 yearsI 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 about 6 yearsThis 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 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 about 6 yearsTry this:
BASE=https://playertest.longtailvideo.com/adaptive/oceans_aes/; 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 about 6 yearsThanks for the response, I was able to download the video last week with hlsdl, github.com/selsta/hlsdl
-
pensnarik about 4 yearsThank 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?