• How to use the internal method of AES-128 encryption to secure a live or VOD stream to sent to Apple iOS devices (ModuleEncryptionHandlerCupertinoStreaming)

    This article provides step-by-step instructions for using the internal method to configure secure streaming of a live or video on demand (VOD) stream to Apple iOS devices.

    Notes:
    • This module has been removed from the Wowza Module Collection because it is only an example that shows how to encrypt a stream. Before it can be used in production, the module needs extra functionality added to determine if a player is allowed to receive the decryption key.
    • The protocol and security specifics for iOS devices are covered in detail in the Internet Engineering Task Force draft-pantos-http-live-streaming-05 specification.


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

    Start by configuring a Wowza Streaming Engine application for streaming to the iPhone. See the Wowza Streaming Engine User's Guide (PDF) or our Tutorials.

    The module below is a skeleton example of how to implement the internal method for delivering an AES-128 encryption key. The following methods control the encryption process:

    • The onHTTPCupertinoEncryptionKeyRequest method is used to control access to the encryption key. This method is unfinished and requires additional code to protect the key. Use this method to add logic to check the query string, cookies, or other request values to control access to the encryption key.

    • The onHTTPCupertinoEncryptionKeyCreateLive method is called for each live HLS playback session. Use this method to control the key that is used for encryption. This method creates a unique MD5 hash that is based on a shared secret and the context path of the stream. This will guarantee that your encryption keys are unique.

    • The onHTTPCupertinoEncryptionKeyCreateVOD method is called for each video on demand HLS session. This method generates a unique encryption key per session, based on a shared secret, the session ID, and the context path of the stream.

    Code:
    package com.wowza.wms.example.module;
    
    import com.wowza.util.*;
    import com.wowza.wms.http.*;
    import com.wowza.wms.httpstreamer.cupertinostreaming.httpstreamer.*;
    import com.wowza.wms.module.*;
    import com.wowza.wms.application.*;
    
    public class ModuleEncryptionHandlerCupertinoStreaming extends ModuleBase
    {
    	public void onHTTPCupertinoEncryptionKeyRequest(HTTPStreamerSessionCupertino httpCupertinoStreamingSession, IHTTPRequest req, IHTTPResponse resp)
    	{
    		boolean isGood = true;
    		
    		String ipAddress = httpCupertinoStreamingSession.getIpAddress();
    		String queryStr = req.getQueryString();
    		String referrer = httpCupertinoStreamingSession.getReferrer();
    		String cookieStr = httpCupertinoStreamingSession.getCookieStr();
    		String userAgent = httpCupertinoStreamingSession.getUserAgent();
    		String sessionId = httpCupertinoStreamingSession.getSessionId();
    		
    		IApplicationInstance appInstance = httpCupertinoStreamingSession.getAppInstance();
    		String streamName = httpCupertinoStreamingSession.getStreamName();
    		
    		// reject encryption key requests that are not delivered over SSL
    		//if (!req.isSecure())
    		//	isGood = false;
    
    		getLogger().info("ModuleEncryptionHandlerCupertinoStreaming.onHTTPCupertinoEncryptionKeyRequest["+appInstance.getContextStr()+"/"+httpCupertinoStreamingSession.getStreamName()+"]: accept:"+isGood);
    		
    		if (!isGood)
    			httpCupertinoStreamingSession.rejectSession();
    	}
    	
    	public void onHTTPCupertinoEncryptionKeyCreateLive(IApplicationInstance appInstance, String streamName, byte[] encKey)
    	{
    		String mySharedSecret = appInstance.getProperties().getPropertyStr("cupertinoEncryptionSharedSecret", "");
    		
    		String hashStr = mySharedSecret+":"+appInstance.getApplication().getName()+":"+appInstance.getName()+":"+streamName;
    
    		byte[] tmpBytes = MD5DigestUtils.generateHashBytes(hashStr);
    		if (tmpBytes != null)
    			System.arraycopy(tmpBytes, 0, encKey, 0, encKey.length);
    		
    		getLogger().info("ModuleEncryptionHandlerCupertinoStreaming.onHTTPCupertinoEncryptionKeyCreateLive["+appInstance.getContextStr()+"/"+streamName+"]: *"+BufferUtils.encodeHexString(encKey).substring(28));
    	}
    
    	public void onHTTPCupertinoEncryptionKeyCreateVOD(HTTPStreamerSessionCupertino httpCupertinoStreamingSession, byte[] encKey)
    	{
    		String ipAddress = httpCupertinoStreamingSession.getIpAddress();
    		String queryStr = httpCupertinoStreamingSession.getQueryStr();
    		String referrer = httpCupertinoStreamingSession.getReferrer();
    		String cookieStr = httpCupertinoStreamingSession.getCookieStr();
    		String userAgent = httpCupertinoStreamingSession.getUserAgent();
    		
    		IApplicationInstance appInstance = httpCupertinoStreamingSession.getAppInstance();
    		String streamName = httpCupertinoStreamingSession.getStreamName();
    		String sessionId = httpCupertinoStreamingSession.getSessionId();
    
    		String mySharedSecret = appInstance.getProperties().getPropertyStr("cupertinoEncryptionSharedSecret", "");
    		
    		String hashStr = mySharedSecret+":"+(httpCupertinoStreamingSession.isHTTPOrigin() ? "" : sessionId+":")+appInstance.getApplication().getName()+":"+appInstance.getName()+":"+httpCupertinoStreamingSession.getStreamName();
    				
    		byte[] tmpBytes = MD5DigestUtils.generateHashBytes(hashStr);
    		if (tmpBytes != null)
    			System.arraycopy(tmpBytes, 0, encKey, 0, encKey.length);
    		
    		getLogger().info("ModuleEncryptionHandlerCupertinoStreaming.onHTTPCupertinoEncryptionKeyCreateVOD["+appInstance.getContextStr()+"/"+httpCupertinoStreamingSession.getStreamName()+"]: *"+BufferUtils.encodeHexString(encKey).substring(28));
    	}
    
    }
    Note: This example will encrypt only your iOS streams. If you want finer control over the session, you will have to use the Eclipse IDE to complete and compile this module.
    Add the following module definition as the last entry in the <Modules> list in [install-dir]/conf/[application]/Application.xml:
    Code:
    <Module>
    	<Name>ModuleEncryptionHandlerCupertinoStreaming</Name>
    	<Description>ModuleEncryptionHandlerCupertinoStreaming</Description>
    	<Class>com.wowza.wms.example.module.ModuleEncryptionHandlerCupertinoStreaming</Class>
    </Module>
    Add the following properties to the application-level <Properties> container at the bottom of [install-dir]/conf/[application]/Application.xml (be sure to get the correct <Properties> container; there are several in the file):
    Code:
    <Property>
    	<Name>cupertinoEncryptionBaseURL</Name>
    	<Value>http://[wowza-ip-address]:1935</Value>
    </Property>
    <Property>
    	<Name>cupertinoEncryptionSharedSecret</Name>
    	<Value>[enckeysharedsecret]</Value>
    </Property>
    <Property>
    	<Name>cupertinoEncryptionLiveRepeaterSharedSecret</Name>
    	<Value>[mysharedsecret]</Value>
    </Property>
    Add the following property to the HTTPStreamer/Properties container in [install-dir]/conf/[application]/Application.xml:
    Code:
    <Property>
    	<Name>cupertinoEnableOnEncKey</Name>
    	<Value>true</Value>
    	<Type>Boolean</Type>
    </Property>
    • The value of the cupertinoEncryptionBaseURL property is the base URL that is used to fetch the encryption key. If you have configured SSL, be sure to leave out the port number in the URL (:1935), change the URL prefix from http:// to https://, and use the domain name rather than the IP address to address the Wowza Streaming Engine.

    • The value of cupertinoEncryptionSharedSecret is the shared secret that is used in the hash algorithm to generate unique encryption keys. The sample code above illustrates how this value is used. An example shared secret is 1ghtY6D3Wgn.

    • The value of cupertinoEncryptionLiveRepeaterSharedSecret is the shared secret that is used when the encryption key is sent to an edge server in a live stream repeater (origin/edge) configuration. When configuring a live stream repeater edge to deliver a live stream, be sure to set cupertinoEncryptionLiveRepeaterSharedSecret to the same value. An example value is 3Er5sWl09xfE).

    • The property cupertinoEnableOnEncKey turns on the internal key delivery handler (in Wowza Media Server™ 3.1 or later)


    If you use this method with Wowza nDVR, the properties are slightly different. Add the following properties to the DVR <Properties> container in [install-dir]/conf/[application]/Application.xml (be sure to get the correct <Properties> container; there are several in the file):
    Code:
    <Property>
    	<Name>cupertinoEncryptionBaseURL</Name>
    	<Value>http://[wowza-ip-address]:1935</Value>
    </Property>
    <Property>
    	<Name>cupertinoEncryptionSharedSecret</Name>
    	<Value>[enckeysharedsecret]</Value>
    </Property>
    Add the following property to the HTTPStreamer/Properties container in [install-dir]/conf/[application]/Application.xml:
    Code:
    <Property>
    	<Name>cupertinoEnableOnEncKey</Name>
    	<Value>true</Value>
    	<Type>Boolean</Type>
    </Property>
    To validate encryption for live streaming, you will see log messages similar to the following example in the Streaming Engine access log, when the stream is published:

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

    In video on demand streaming, you will see a log message similar to the following in the Streaming Engine access log, when a playback session starts:

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

    Notes:
    • To test AES encryption, see How to test AES encryption for Apple HLS streams.
    • For delivery of the encryption key, it is best to configure a <HostPort> in [install-dir]/conf/VHost.xml that uses SSL encryption. This will protect the encryption key from being intercepted in transit. See the Wowza Streaming Engine User's Guide for more information about SSL configuration. If you do configure a port that uses SSL encryption, be sure to uncomment the if (!req.isSecure()) check in onHTTPCupertinoEncryptionKeyRequest to ensure that the SSL port is used to deliver the key.
    For nDVR in origin-edge mode, both origin and edges use a common shared secret string to encrypt data sent between instances. The dvrEncryptionSharedSecret or liveRepeaterEncryptionSharedSecret properties may be used to customize the shared secret that is used. See the nDVR advanced configuration article for specific uses of these properties.

    Wowza media server software and all components, including modules, source code, and other related items offered on this page, are copyrighted 20062014 by Wowza Media Systems, LLC, all rights reserved, and are licensed pursuant to the Wowza Media Software End User License Agreement.
    Updated: For Wowza Streaming Engine 4.0.6 on 08112014.
    Comments 56 Comments
    1. charlie -
      Actually, we do support key rotation it is just not documented. Hope to document it in the next few weeks.

      Charlie
    1. Gpolox -
      "cupertinoEnableOnEncKey: (Wowza Media Server 3.1 or greater) Turns on internal key deliver handler."
      what does this mean ? I need to set cupertinoEnableOnEncKey for wowza 3.1 but older versions will work without it or the whole feature is supported only for 3.1 ? As I see comments created before wowza 3.1 it's not clear should I set cupertinoEnableOnEncKey for 3.0.5 for example
    1. rrlanham -
      This is a new, optional feature. The wording in the release notes is a misleading; we'll work on that. AES with a previous version will continue to work as-is.

      Richard
    1. charlie -
      Quote Originally Posted by Gpolox View Post
      "cupertinoEnableOnEncKey: (Wowza Media Server 3.1 or greater) Turns on internal key deliver handler."
      what does this mean ? I need to set cupertinoEnableOnEncKey for wowza 3.1 but older versions will work without it or the whole feature is supported only for 3.1 ? As I see comments created before wowza 3.1 it's not clear should I set cupertinoEnableOnEncKey for 3.0.5 for example
      In 3.0.5 and prior versions you did not have to set any property to enable the use the internal key deliver mechanism. In Wowza Server 3.1 or greater you do have to add this property to use the internal key delivery mechanism.

      Charlie
    1. vu duc -
      Hi,

      I've a problem:
      I've created a self-signed cert, when i play video using smooth streaming and flash HDS, it's ok:
      https://192.168.1.156:443/vod/sample.mp4/Manifest
      https://192.168.1.156:443/vod/sample.mp4/manifest.f4m

      But when I play video using HLS on VLS, it can't play:
      https://192.168.1.156:443/vod/mp4:sa.../playlist.m3u8
      VLC has error: "Your input can't be opened:
      VLC is unable to open the MRL 'https://192.168.1.156:443/vod/mp4:sample.mp4/playlist.m3u8'. Check the log for details"

      Please help,
      Thanks

      Duc
    1. rrlanham -
      All I can suggest is update VLC. I think support for HLS streams in VLC is pretty new.

      Richard
    1. bobmane -
      for VOD hls

      re: onHTTPCupertinoEncryptionKeyCreateVOD or internal method in general.... is correct all i have to do is download module and install and will be similar to RTMPe but for HLS - but for additional security I would have to modify module ?
    1. rrlanham -
      Right

      R
    1. friendly12345 -
      Quote Originally Posted by charlie View Post
      Actually, we do support key rotation it is just not documented. Hope to document it in the next few weeks.

      Charlie
      How is the Key Rotation document status.
    1. bobmane -
      " If you want finer control over the session you will have to complete and compile this module using the Wowza IDE. "

      what finer control is achieved doing this?
    1. rrlanham -
      Answered in ticket
    1. bobmane -
      not answered

      "Actually, we do support key rotation it is just not
      documented. Hope to document it in the next few weeks." - Charlie
    1. yuvrajsinh -
      Hello,

      I want to secure playback of nDVR recording, but don't know how to achieve it, since I am new to streaming stuff.

      I have configured wowza for nDVR recording, and it is working fine with playback. (Help from : http://www.wowza.com/forums/content....0#nDVRFunction).

      I am playing this recording using "LiveDvrStraming" example app and it's playing also.

      But now i want to give security for this. can u help me regarding this ?
    1. rrlanham -
      You should be able to use DRM options
      http://www.wowza.com/forums/content....don-wowza-ndvr

      If you are streaming to iOS you can use AES
      http://www.wowza.com/forums/content....ternal-method)

      If you are streaming to Flash HTTP or Silverlight you can use HTTPS, there is more info in the Security Overview
      http://www.wowza.com/forums/content....urity-overview

      And you can do this to control access:
      http://www.wowza.com/forums/content....josestreaming)

      Richard
    1. f4cl3y -
      The encryption is based on the session id only? can I generate a key based on a file or a stream suitable for caching?

      thanx
    1. Samiarn -
      So basically i followed the steps, tested the stream, Working!

      Where do i set "rules" of only (These allowed devices) should be able to watch the stream?