• How to secure Apple HTTP Live Streaming (AES-128 - external method)

    This article provides step-by-step instructions for using the external method in Wowza Streaming Engine™ software to configure secure streaming of a live or video on demand (VOD) stream to Apple iOS devices.

    Notes:


    When you use the external method of AES-128 encryption, encryption keys are delivered by using an external URL. There is also an internal method that uses Wowza Streaming Engine to deliver all media chunks and encryption keys. For more information about how to use the internal method, see How to use the internal method of AES-128 encryption to secure a live or VOD stream to sent to Apple iOS devices (ModuleEncryptionHandlerCupertinoStreaming).

    Configuration

    1. Start by configuring a Wowza Streaming Engine application for streaming by following one of our video on demand or live streaming tutorials. You will also need access to a web server.

    2. Create a key file in the [install-dir]/keys folder for each stream you want to encrypt. The key file is a text file with the name of the stream you want to protect and a .key extension. For example, to encrypt a live stream named myStream, create a key file in the [install-dir]/keys folder named myStream.key with content similar to the following:
      Code:
      cupertinostreaming-aes128-key: DE51A7254739C0EDF1DCE13BBB308FF0
      cupertinostreaming-aes128-url: http:/mycompany.com/security.aspx
      • cupertinostreaming-aes128-key is a 32-byte key that will match the key used to decrypt the stream. You will want to replace this key with one of your own.
      • cupertinostreaming-aes128-url is the URL that devices will use to fetch the key to decrypt the stream, as shown in the examples below. You can control who can view your stream by managing access to this URL.

      You must have a .key file for each stream that you want to protect. Each of these files may use the same 32-byte key and the same URL to return that key to devices, or you might have a different key and different URL for each stream.

      Note: You can also use the genkey utility to generate key files. A key file with a different key is generated each time you run this utility. You can integrate this utility into an automated workflow to create many .key files for a video library.


    The key must be returned to a device as a packed array of 16 octets in binary format with the following header information:
    Code:
    Content-Type: binary/octet-stream
    Pragma: no-cache

    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 of these examples includes a Boolean isValid value that defaults to true. You can modify these examples to provide your own security tests to validate that the user can access the content. If the user should not be allowed to access the content, you can block them from receiving the decryption key by setting the isValid value to false.

    If the request for this key returns a status of 403, then the device cannot decrypt and play the stream. If the key is returned, then the stream will be decrypted and played. Require HTTPS access to this key so that it is not sent over an unsecured connection on the Internet.

    The key being sent in these examples is DE51A7254739C0EDF1DCE13BBB308FF0. Substitute this value with the 32-byte key set in the .key file in the Configuration section.

    Note: These examples are provided as-is with no expressed warranty. You can modify or distribute them without restriction.

    ASP.NET example

    Code:
    <%@ 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

    Code:
    <%@ 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

    Code:
    <?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
    }
    
    ?>
    To validate encryption for a live stream, you will see log messages in the Wowza access log similar to the following when the stream is published:

    LiveStreamPacketizerCupertino.init[live/_definst_/myStream]: Encrypt Cupertino stream: key: *55BB url: http://192.168.1.120:1935/live/_definst_/myStream/key{sessionid}.m3u8key

    To validate encryption for video on demand streaming, you will see a log message similar to the following example in the Wowza access log when an HLS (cupertino) playback session starts:

    HTTPStreamerCupertinoIndexFile.init[vod/_definst_/sample.mp4]: Encrypt Cupertino stream: key: *763a url: http://192.168.1.120:1935/vod/_definst_/mp4:sample.mp4/key{bitrate}{sessionid}.m3u8key

    Using genkey

    Wowza Streaming Engine software includes the genkey utility that you can use to generate key files.

    1. Open a command shell and change directory to [install-dir]/bin.

    2. Enter the following command:

      Windows:
      Code:
      genkey.bat iphone [stream-name] [key-url]
      Linux:
      Code:
      ./genkey.sh iphone [stream-name] [key-url]
      Where [stream-name] is the name of the stream that will be encrypted and [key-url] is the URL that an iOS device will use to receive the key data (see specifics about the [key-url] below). This command will generate the file [stream-name].key. An example command line is:
      Code:
      genkey.bat iphone myStream https://mycompany.com/myStream.php
      The output should look this:
      Code:
      key: B9A954F22ACD093BDEB80622EC7155BB
      url: https://mycompany.com/myStream.php
      
      Key file generated: myStream.key
      Copy file to: [install-dir]/keys
    3. Copy the file generated in the previous step. You will find this file in the [install-dir]/bin folder. Copy it to the [install-dir]/keys folder. This will protect a stream with that name in any application. See below for details about how to set up an application-specific keys folder.


    keys folder

    By default, all applications are configured to use the same keys folder, which can lead to stream name conflicts. To configure a keys folder on a per-application basis, open [install-dir]/conf/[application]/Application.xml in a text editor and make the Streams/KeyDir property value empty.
    Code:
    <KeyDir></KeyDir>
    When KeyDir is empty, Wowza Streaming Engine will look in [install-dir]/applications/[application]/keys/[appInstance] for your .key files. You can also use the StorageDir path variables shown in Application.xml to construct other locations.

    Key URL

    Query parameters passed as part of the playlist.m3u8 URL (as well as the wowzasessionid) will be passed to the key URL. For example, if you play a stream using the following URL:

    http://[wowza-ip-address]:1935/live/myStream/playlist.m3u8?userid=12345

    And the key URL is defined as the following URL:

    https://mycompany.com/myStream.php?keyinfo=securekey

    Then the URL sent to myStream.php will include the following query parameters:

    https://mycompany.com/myStream.php?keyinfo=securekey&userid=12345&wowzasessionid=345234

    Note: To test AES encryption, see How to test AES encryption for Apple HLS streams.

    Updated: For Wowza Streaming Engine 4.0 on 02-11-2014

    Comments 78 Comments
    1. rrlanham -
      akilouise,

      No, sorry, I don't yet.

      Richard
    1. rrlanham -
      But I will ask about it tomorrow

      Richard
    1. rrlanham -
      It still needs to be documented. I don't have a time frame for this. Check this group of articles:

      http://www.wowza.com/forums/content....ddon-wowza-drm

      It will show up there when it is published.

      Richard
    1. sdahdaah -
      Quote Originally Posted by dwoodwoo View Post
      I'm trying to set up a Cupertino VOD file for simple secure streaming to a iPad application in Phonegap 1.2.0.

      My local ip is 10.120.1.157 so I'll use that for my example. I've encrypted the file sampleblocked.mp4 using
      ./genkey.sh ipad sampleblocked.mp4 http://10.120.1.157:1935

      Returning:
      cupertinostreaming-aes128-key: F96B05094642E835D09AF434CFD55DE1
      cupertinostreaming-aes128-url: http://10.120.1.157:1935/video.php


      I've moved the resulting key file sampleblocked.mp4.key into the [wowzaroot]/keys directory.

      Where I'm confused is what I should use to call the encrypted VOD file so it will play.

      If I use:

      <video controls src="http://10.120.1.157:1935/vod/_definst_/mp4:sampleblocked.mp4/playlist.m3u8" width="480" height="360"></video><br />

      the iPad app appropriately returns "The operation could not be completed"

      But I don't understand what code should be used to get this file to play.

      I assumed it might be:
      http://10.120.1.157:1935/video.php?k...9AF434CFD55DE1

      but that doesn't work.

      Thanks for any pointers and best regards--

      Dennis
      Haveyou been able to solve this? please can you post the solution how to call the video from wowza with key send step by step.
    1. versatil -
      Quote Originally Posted by rrlanham View Post
      It still needs to be documented. I don't have a time frame for this. Check this group of articles:

      http://www.wowza.com/forums/content....ddon-wowza-drm

      It will show up there when it is published.

      Richard
      I understand you may not yet have a time frame for when documentation for key rotation will go up. Just posting to let you know there's at least one more party interested in this.

      Will keep checking that group of articles over time.

      Thanks.
    1. rrlanham -
      Thanks,

      Richard
    1. mcaron -
      Hi,
      I wonder what parameters should be passed to genkey if I want to have AES encryption for smil that contains several live streams:

      Code:
      http://[wowza-address]:1935/hls/smil:multistream.smil/playlist.m3u8
      
      genkey iphone multistream.smil [key-url] ??
      or do I have to create separate key for each stream?
      Best!
    1. chatlumo -
      Quote Originally Posted by mcaron View Post
      Hi,
      I wonder what parameters should be passed to genkey if I want to have AES encryption for smil that contains several live streams:

      Code:
      http://[wowza-address]:1935/hls/smil:multistream.smil/playlist.m3u8
      
      genkey iphone multistream.smil [key-url] ??
      or do I have to create separate key for each stream?
      Best!
      @mcaron, did you find how to proceed ?
    1. rrlanham -
      It has to be done for each stream in the smil. You don't do it for the smil itself, which is just a text file the playback client uses to know what streams are available.

      Richard
    1. chatlumo -
      Quote Originally Posted by rrlanham View Post
      It has to be done for each stream in the smil. You don't do it for the smil itself, which is just a text file the playback client uses to know what streams are available.

      Richard
      Ok, so i need to create a key for each video ?
      Not the possibility to have 1 key for all videos for the same application ?
    1. rrlanham -
      I think you can use the same key for each stream, as I remember in my tests.

      Richard
    1. chatlumo -
      So, i can use anything term for "[stream-name]" and not need to be equal to video filename ?
    1. gearup -
      I have a basic question regarding the event sequence in external method security.

      I have a password protected page: http://URL1 which loads the HTML5 player in the customer's IPhone/IPad to play http://IP:1935/application/mystream

      I have script https://URL2 which returns the key generated in /keys/mystream.key generated by genkey.bat

      I am trying to confirm that my server will only stream to the HTML5 player in http://URL1

      Where is https://URL2 called from to prevent direct linking to http://IP:1935/application/mystream from anywhere other than http://URL1?
    1. rrlanham -
      Wowza calls the external script when the HLS session is started. If it does not respond with the key the session will not work.

      Richard
    1. gearup -
      What means would the external script have to know if Wowza is in an approved or pirated session?

      Quote Originally Posted by rrlanham View Post
      Wowza calls the external script when the HLS session is started. If it does not respond with the key the session will not work.

      Richard
    1. streetofdreams -
      Hello Rchard-san,

      This is Iwata.

      I am afraid Iam not good at English.

      I have same problem too.

      now status is the following.

      (1) WMS3.0 + Transcoder (in One server)
      => external encrypt on live stream is OK.
      safari on iOS was normal requested to CGI URL in key file.
      CGI is set on WMS3.0+Transcoder.

      (2) WMS3.0 + Transcoder (origin) -> WMS3.5(edge)
      => external encrypt is NG.
      this pattern,
      safari on iOS was not requested to CGI URL in key file.
      CGI is set on WMS3.5(edge).
      but, I was look the following log in wowzamediaserver_access.log.
      ------------------
      INFO server comment - LiveStreamPacketizerCupertino.init[liveedge/_definst_/recsmart.stream_240p_test]:
      Encrypt Cupertino stream: key: *F367 url: http://&#91;WMS3.5(edge)IPaddress]/cgi-b..._240p_test.cgi
      ------------------

      Moreover, about (2) , we were set the vod and key files to the server.
      but it was successfull.

      Please help me solving about (2) problem.


      configration of (2)"WMS3.5(edge) is the following.

      [installdir]/conf/liveedge/Application.xml
      <Streams>
      <StreamType>liverepeater-edge</StreamType>
      <StorageDir>${com.wowza.wms.context.VHostConfigHome}/content</StorageDir>
      <KeyDir>${com.wowza.wms.context.VHostConfigHome}/keys</KeyDir>
      <LiveStreamPacketizers>cupertinostreamingrepeater,smoothstreamingrepeater,sanjosestreamingrepeater </LiveStreamPacketizers>
      <Properties>
      </Properties>
      </Streams>

      <HTTPStreamers>cupertinostreaming,smoothstreaming,sanjosestreaming</HTTPStreamers>

      <Repeater>
      <OriginURL>wowz://[(1)WMS3.0+Tanscoder IPAddress]:1935/live</OriginURL>
      <QueryString><![CDATA[]]></QueryString>
      </Repeater>


      [installdir]/keys/recsmart.stream_240p_test.key
      cupertinostreaming-aes128-key: F88BB4132F4A864FE9D4EC2918BEF367
      cupertinostreaming-aes128-url: http://&#91;(2)WMS3.5(edge)IPAddress]/cg..._240p_test.cgi

      Best regards,
      Iwata


      Quote Originally Posted by rrlanham View Post
      What is [stream-name] ? Is it a simple name like "myStream", and you have the rtmp address of the origin in /originURL?

      If so, try using a .stream file instead. Create a text file named origin.stream in the content folder:

      /content/origin.stream, with contents:
      rtmp://[wowza-address]:1935/liveorigin/_definst_/myStream

      Now use stream name "origin.stream"

      Richard
    1. rrlanham -
      Hi Iwata,

      I'm not sure what the problem is. Is the .cgi script reachable?

      Does streaming work? I assume that AES encryption is not working...?

      Richard
    1. streetofdreams -
      Hi Richard,

      Thank you very much for reply.

      .cgi script is reachable.

      I was searched on access.log of web server, but I not found for .cgi access log.

      but I was access successfull to this cgi via Web browser.

      Moreover, I tried same method to vod contents, but it is successfull.

      Is this external encrypt method not support to liveedge?

      Best regards,
      Iwata

      Quote Originally Posted by rrlanham View Post
      Hi Iwata,

      I'm not sure what the problem is. Is the .cgi script reachable?

      Does streaming work? I assume that AES encryption is not working...?

      Richard
    1. streetofdreams -
      Hi Richard,

      >Does streaming work? I assume that AES encryption is not working...?

      streaminig is working.

      and it is successfull by the following flow.

      WMS3.0+TranscoderAddon(live) --> WMS3.5(liveedge)-->(HLS without external AES Encrypt)--> iOS6.X

      Best regards,
      Iwata


      Quote Originally Posted by rrlanham View Post
      Hi Iwata,

      I'm not sure what the problem is. Is the .cgi script reachable?

      Does streaming work? I assume that AES encryption is not working...?

      Richard