Encrypt HLS streams

View Markdown

Unprotected HLS segments can be downloaded and redistributed trivially. “Masterwork” (a paid video course platform like MasterClass) needs to encrypt stream segments so only authenticated viewers can decrypt and play them.

API

ittybit adaptive \
  -i https://masterwork-app.com/courses/photography/lesson-1.mov \
  --format hls \
  --encryption aes-128 \
  --key-url https://masterwork-app.com/keys/photography/lesson-1
const task = {
  input: 'https://masterwork-app.com/courses/photography/lesson-1.mov',
  kind: 'adaptive_video',
  options: {
    format: 'hls',
    encryption: {
      method: 'aes-128',
      key_url: 'https://masterwork-app.com/keys/photography/lesson-1',
    },
  },
};

const res = await fetch('https://api.ittybit.com/jobs', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.ITTYBIT_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(task),
});
const data = await res.json();
import requests

task = {
    "input": "https://masterwork-app.com/courses/photography/lesson-1.mov",
    "kind": "adaptive_video",
    "options": {
        "format": "hls",
        "encryption": {
            "method": "aes-128",
            "key_url": "https://masterwork-app.com/keys/photography/lesson-1",
        },
    },
}

res = requests.post(
    "https://api.ittybit.com/jobs",
    headers={"Authorization": f"Bearer {api_key}"},
    json=task,
)
data = res.json()
TASK='{
  "input": "https://masterwork-app.com/courses/photography/lesson-1.mov",
  "kind": "adaptive_video",
  "options": {
    "format": "hls",
    "encryption": {
      "method": "aes-128",
      "key_url": "https://masterwork-app.com/keys/photography/lesson-1"
    }
  }
}'

curl -X POST https://api.ittybit.com/jobs \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$TASK"

Each .ts segment is encrypted with AES-128. The manifest includes an #EXT-X-KEY tag pointing to key_url. The player fetches the key before decrypting segments.

CLI

ittybit adaptive \
  -i lesson-1.mov \
  -o lesson-1.m3u8 \
  --encryption aes-128 \
  --key-url https://masterwork-app.com/keys/photography/lesson-1

How AES-128 HLS encryption works

  1. Ittybit generates a random 128-bit encryption key
  2. Each segment is encrypted with that key using AES-128-CBC
  3. The manifest references key_url — the endpoint your server exposes to serve the key
  4. The player requests the key, then decrypts segments on the fly

Securing the key endpoint

The encryption is only as strong as the key delivery. Your key_url endpoint must:

RequirementWhy
Require authenticationPrevents anonymous key retrieval
Use HTTPSPrevents key interception in transit
Check entitlementOnly serve keys to users who purchased the course
Set short cache headersLimits key exposure window

Example: check the session cookie or JWT, verify the user has access to the course, then return the raw 16-byte key with Content-Type: application/octet-stream.

Encryption methods

MethodBrowser supportProtection level
AES-128All HLS playersGood — prevents casual copying
SAMPLE-AESSafari, some othersBetter — encrypts NAL units

AES-128 is the most widely supported option and sufficient for most paid content platforms. For premium content requiring stronger DRM, consider FairPlay (Apple) or Widevine (Google) integrations.