Sample-Level AES-128 HLS Encryption with FFmpeg
This article provides a direct guide on securing HTTP Live Streaming (HLS) streams using AES-128 encryption with FFmpeg. It explains the requirements for encryption keys, how to construct the necessary key information file, the precise FFmpeg commands for generating encrypted segments, and the technical distinction between standard segment-level AES-128 and sample-level (SAMPLE-AES) encryption workflows.
Understanding AES-128 vs. SAMPLE-AES
HLS supports two primary forms of AES-128 encryption:
- Segment-Level (AES-128): The entire media segment (Transport Stream or fragmented MP4) is encrypted using cipher block chaining (CBC). This is natively supported by FFmpeg's built-in HLS muxer.
- Sample-Level (SAMPLE-AES): Individual audio and video media samples within the container are encrypted, leaving container headers in plain text. This is commonly required for FairPlay Streaming DRM.
Native FFmpeg provides built-in support for segment-level
AES-128. True SAMPLE-AES requires fragmented
MP4 (fMP4) packaging and is typically handled either by piping FFmpeg
output to a dedicated packager (such as Bento4 or Shaka Packager) or
using FFmpeg to generate standard AES-128 encrypted HLS.
Step 1: Generate the Encryption Key and IV
AES-128 requires a 16-byte (128-bit) binary key and an optional 16-byte Initialization Vector (IV). Generate these using OpenSSL:
# Generate a random 16-byte binary key
openssl rand 16 > enc.key
# Generate a 16-byte hexadecimal IV (32 hex characters)
openssl rand -hex 16 > enc.ivStep 2: Create the Key Info File
FFmpeg uses a key_info_file to map the key location,
URI, and IV into the generated M3U8 playlist.
Create a plain text file named enc.keyinfo containing
the following structure:
https://example.com/keys/enc.key
enc.key
0123456789abcdef0123456789abcdef
- Line 1: The public URI where the player will fetch the key.
- Line 2: The local path to
enc.keyso FFmpeg can read it during encoding. - Line 3 (Optional): The hexadecimal IV. If omitted, FFmpeg uses the segment sequence number as the IV.
Step 3: Run the FFmpeg Command
Execute FFmpeg with the -hls_key_info_file option to
encrypt segments during packaging:
ffmpeg -i input.mp4 \
-c:v libx264 -b:v 2000k \
-c:a aac -b:a 128k \
-hls_time 6 \
-hls_playlist_type vod \
-hls_key_info_file enc.keyinfo \
-hls_segment_filename "segment_%03d.ts" \
master.m3u8The resulting master.m3u8 playlist will contain the
#EXT-X-KEY tag instructing the player to decrypt each
segment:
#EXT-X-KEY:METHOD=AES-128,URI="https://example.com/keys/enc.key",IV=0x0123456789abcdef0123456789abcdef
Step 4: True Sample-Level Encryption (SAMPLE-AES Workflow)
If your client architecture strictly mandates
METHOD=SAMPLE-AES (e.g., Apple FairPlay), FFmpeg's native
hls_key_info_file will not suffice alone because it outputs
METHOD=AES-128.
To achieve true sample-level encryption, encode to an intermediate fragmented MP4 using FFmpeg, then pass the file to a specialized packager:
# Encode to fMP4 with FFmpeg
ffmpeg -i input.mp4 -c:v libx264 -c:a aac -movflags frag_keyframe+empty_moov intermediate.mp4
# Encrypt using Bento4 for SAMPLE-AES HLS packaging
mp4hls --encryption-mode=SAMPLE-AES \
--encryption-key=0123456789abcdef0123456789abcdef:enc.key \
--encryption-key-uri=https://example.com/keys/enc.key \
intermediate.mp4This ensures the manifest outputs
#EXT-X-KEY:METHOD=SAMPLE-AES with payload-level encryption
compatible with hardware-accelerated DRM frameworks.