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 internal method of AES-128 encryption, all media chunks and encryption keys are delivered by Wowza Media Server. There is also an external method in which the encryption key is delivered 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 Media Server application for streaming to the iPhone. See the Quick Start Guide, Wowza Media Server User's Guide, 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. It is here that you add logic to check the query string, cookies, or other request values to control access to the encryption key.
- The onHTTPCupertinoEncryptionKeyCreateLive method is called each time a live stream is started and is called once per live stream. It is here that you can control the key that is used for encryption. You can see in this method we are creating 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 per-video on demand session. Here we are generating 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.plugin.collection.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+":"+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: The collection version will just encrypt your iOS streams. If you want finer control over the session, you will have to complete and compile this module using the Wowza IDE.
Next, 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.plugin.collection.module.ModuleEncryptionHandlerCupertinoStreaming</Class> </Module>
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>
Code:
<Property> <Name>cupertinoEnableOnEncKey</Name> <Value>true</Value> <Type>Boolean</Type> </Property>
- cupertinoEncryptionBaseURL 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 IP address to address the Wowza Media Server.
- 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.
- 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).
- cupertinoEnableOnEncKey turns on the internal key delivery handler. (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> <!-- This shared secret is for origin-edge and should be on both origin edge --> <Property> <Name>dvrEncryptionSharedSecret</Name> <Type>String</Type> <Value>[originedgesharedsecret]</Value> </Property>
Code:
<Property> <Name>cupertinoEnableOnEncKey</Name> <Value>true</Value> <Type>Boolean</Type> </Property>
Note: To test AES encryption, see How to test AES encryption for Apple HLS streams.
Note: 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 Media Server 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.
- Click here, if you are having problems or would like to discuss this article.
- Leave a comment below, if there is some aspect of this article you would like to see changed or improved.


Article List
Categories
Wowza Media