Encrypt HLS streams
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-1const 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
- Ittybit generates a random 128-bit encryption key
- Each segment is encrypted with that key using AES-128-CBC
- The manifest references
key_url— the endpoint your server exposes to serve the key - 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:
| Requirement | Why |
|---|---|
| Require authentication | Prevents anonymous key retrieval |
| Use HTTPS | Prevents key interception in transit |
| Check entitlement | Only serve keys to users who purchased the course |
| Set short cache headers | Limits 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
| Method | Browser support | Protection level |
|---|---|---|
| AES-128 | All HLS players | Good — prevents casual copying |
| SAMPLE-AES | Safari, some others | Better — 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.