Secure HLS streams with AES-128 external encryption using the Wowza Video REST API
The Wowza Video™ service allows you to secure HLS streams using the external method of AES-128 encryption. When you use the external method of AES-128 encryption, encryption keys are delivered to devices from an external URL. This article describes how to use the Wowza Video REST API to configure AES-128 encryption for an HLS stream.
Before you start
You should complete the following tasks:
-
Create a
live stream
or a
transcoder
. You'll need the
resulting
live_stream_id
ortranscoder_id
to configure AES-128 encryption. View our connect a source topics to learn how to create a live stream or transcoder for RTMP, RTSP, IP camera, SRT encoder, UDP encoder, WebRTC, and Wowza Streaming Engine.
1. Configure AES-128 encryption
Configure the following transcoder properties to enable AES encryption.
You'll need to first configure the aes128Host
and then the aes128Secret
.
Configure the aes128Host
You can use the following sample request, making sure to:
-
Set
key
toaes128Host
. -
Set
section
tocupertino
. -
Set
value
to the URL that devices will use to fetch the key to decrypt the stream. -
Set
id
to thelive_stream_id
ortranscoder_id
.
Sample request
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${WV_JWT}" \
-d '{
"property": {
"key": "aes128Host",
"section": "cupertino",
"value": "https://[server-url].com/aes128/key.bin"
}
}' "${WV_HOST}/api/${WV_VERSION}/transcoders/[id]/properties"
Sample response
{
"property": {
"key": "aes128Host",
"section": "cupertino",
"value": "https://[server-url].com/aes128/key.bin"
}
}
Configure the aes128Secret
You can use the following sample request, making sure to:
-
Set
key
toaes128Secret
. -
Set
section
tocupertino
. -
Set
value
to the 16-byte key that will be used to decrypt the stream. The key must be 32 characters in length and can only contain hex characters (a-f, A-F, 0-9). The key must match the key returned by theaes128Host
. -
Set
id
to thelive_stream_id
ortranscoder_id
.
Sample request
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${WV_JWT}" \
-d '{
"property": {
"key": "aes128Secret",
"section": "cupertino",
"value": "AaBbCc123456789DdEeFf123456789aa"
}
}' "${WV_HOST}/api/${WV_VERSION}/transcoders/[id]/properties"
Sample response
{
"property": {
"key": "aes128Secret",
"section": "cupertino",
"value": "AaBbCc123456789DdEeFf123456789aa"
}
}
Info
If you have started a transcoder at any point before updating its transcoder property, you must reset the transcoder for the property to take effect. This step isn’t necessary if you haven’t started the transcoder at all.
2. Reset the transcoder
curl -X PUT \
-H "Authorization: Bearer ${WV_JWT}" \
"${WV_HOST}/api/${WV_VERSION}/transcoders/[transcoder_id]/reset"
Examples
The following examples show how to use popular web application
technologies such as ASP.NET, JSP, and PHP to send the key
data. Each example includes a Boolean isValid
value that defaults to
true
. You can modify the examples to provide your own security tests
to validate that users can access the content. If users shouldn't be
allowed to access the content, you can block them from receiving the
decryption key by setting isValid
to false
.
If the request for the key returns a status of 403
, then the device
won't be able to decrypt and play the stream. If the key is returned,
then the stream will be decrypted and played. Require HTTPS access to
this key to ensure that it isn't sent over an unsecured connection on
the Internet.
The key being sent in these examples is
DE51A7254739C0EDF1DCE13BBB308FF0
. You should substitute this value
with a different 16-byte key. The key should match the key value
specified for the aes128Secret
transcoder property.
Info
These examples are provided as-is with no expressed warranty. You can modify or distribute them without restriction.
ASP.NET example
<%@ Page Language="C#" %>
<%
Boolean isValid = true;
if (!isValid)
{
Response.Status = "403 Forbidden";
}
else
{
Response.AddHeader("Content-Type", "binary/octet-stream");
Response.AddHeader("Pragma", "nocache");
String keyStr = "DE51A7254739C0EDF1DCE13BBB308FF0";
int len = keyStr.Length/2;
byte[] keyBuffer = new byte[len];
for (int i=0;i<len;i++)
keyBuffer[i] = Convert.ToByte(keyStr.Substring(i*2, 2), 16);
Response.BinaryWrite(keyBuffer);
Response.Flush();
Response.End();
}
%>
JSP example
<%@ page import="java.util.*,java.io.*" %>
<%
boolean isValid = true;
if (!isValid)
{
response.setStatus( 403 );
}
else
{
response.setHeader("Content-Type", "binary/octet-stream");
response.setHeader("Pragma", "no-cache");
String keyStr = "DE51A7254739C0EDF1DCE13BBB308FF0";
int len = keyStr.length()/2;
byte[] keyBuffer = new byte[len];
for (int i=0;i<len;i++)
keyBuffer[i] = (byte)Integer.parseInt(keyStr.substring(i*2, (i*2)+2), 16);
OutputStream outs = response.getOutputStream();
outs.write(keyBuffer);
outs.flush();
}
%>
PHP example
<?php
// Check if function exists (php5.4+ includes this method)
if(!function_exists("hex2bin")){
function hex2bin($h)
{
if (!is_string($h))
return null;
$r = '';
for ($a=0;$a<strlen($h);$a+=2)
{
$r .= chr(hexdec($h{$a}.$h{($a+1)}));
}
return $r;
}
}
$isValid = true;
if (! $isValid)
{
header('HTTP/1.0 403 Forbidden');
}
else
{
header('Content-Type: binary/octet-stream');
header('Pragma: no-cache');
echo hex2bin('DE51A7254739C0EDF1DCE13BBB308FF0');
exit(); // this is needed to ensure cr/lf is not added to output
}
?>