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

    Step-by-step instructions for configuring secure streaming of a live or video on demand stream to Apple iOS devices using the external method.

    Note: iPhone/iPod touch OS Version 3.0 is required.


    The protocol and security specifics about iOS devices are covered in detail in the Internet Engineering Task Force draft-pantos-http-live-streaming-05 specification.

    With the external method of AES-128 encryption, encryption keys are delivered by using an external URL. Wowza Media Server. There is also an internal method in which all media chunks and encryption keys are delivered using Wowza Media Server. For more information about how to use the internal method, see How to secure Apple HTTP Live Streaming (AES-128, internal method).

    1. Start by configuring an Wowza Media Server application for streaming by following one of the video on demand or live streaming tutorials. You will also need access to a web server.

    2. Configure an AES-128 key for encrypting the stream. We do this using the genkey utility that ships with Wowza Media Server in the [install-dir]/bin folder:

      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 the iOS device will use to receive the key data (see the specifics on 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 by the command-line tool to the [install-dir]/keys folder. See below for details about how to set up an application-specific keys folder.


    3. Start Wowza Media Server and send the live stream to the server. When the live stream is published to the server, you will see the following log messages:
      Code:
      Encrypt iPhone stream: key: *[last-8-characters-of-key]
      Encrypt iPhone stream: url: [key-url]
      This indicates that the stream is being encrypted properly.


    The [key-url] is the URL that the devices will use to fetch the key to decrypt the stream. This is where you can control who can view your stream. 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. We suggest that this URL be protected by HTTPS so that the key is not sent over the Internet in the clear. The key information must be sent as a packed array of 16 octets in binary format with the following header information:
    Code:
    Content-Type: binary/octet-stream
    Pragma: no-cache
    The following examples show how to send the key data using popular web application technologies such as ASP.NET, JSP, and PHP. The key being sent in these examples is DE51A7254739C0EDF1DCE13BBB308FF0. Substitute the key generated above for this value:

    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
    
    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
    }
    
    ?>
    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 can access the content, you can block them from receiving the decryption key by setting the isValid value to false.

    These examples are provided as-is with no expressed warranty. Feel free to modify or distribute them without restriction.

    [install-dir]
    Windows (default): C:\Program Files\Wowza Media Systems\Wowza Media Server [version]
    Mac OS X: /Library/WowzaMediaServer
    Linux: /usr/local/WowzaMediaServer

    Note: 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 change the Streams/KeyDir property value. If you set this value to an empty value (zero length), then it defaults to:
    Code:
    [install-dir]/applications/[application]/keys/[appInstance]

    Note: In Wowza Media Server 2 (preview 7) or later, any query parameters passed as part of the playlist.m3u8 URL as well as the wowza session id will be passed to the key URL. For example, if you play a stream using the URL:
    Code:
    http://[wowza-ip-address]:1935/live/myStream/playlist.m3u8?userid=12345
    And the key URL is defined as:
    Code:
    https://mycompany.com/myStream.php?keyinfo=securekey
    Then the URL sent to myStream.php will include the following query parameters:
    Code:
    https://mycompany.com/myStream.php?keyinfo=securekey&userid=12345&wowzasessionid=345234




    Comments 61 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.VHostConfigHom e}/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