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

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

    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));
    	}
    
    }
    A compiled version of this module is included in the Wowza Modules Collection. Download and unzip the collection, copy the file /lib/wms-plugin-collection.jar from the package to the Wowza Media Server installation folder [install-dir]/lib , and then restart the server.

    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>
    The following properties must be added 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>
    • 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>
    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>

    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.



    Comments 55 Comments
    1. solasilor -
      I am having trouble getting the HTTP secure stream working. Here's what I have done so far:

      Execute the following command to import the Chain Certificate

      keytool -import -alias root -trustcacerts -file /private/etc/certificates/www.prudhub.com.chcrt -keystore www.prudhub.com.cert
      Enter keystore password:
      Re-enter new password:
      Certificate was added to keystore

      Execute the following command to import the new certificate

      keytool -import -alias admin -trustcacerts -file /private/etc/certificates/www.prudhub.com.crt -keystore www.prudhub.com.cert
      Enter keystore password:
      Certificate was added to keystore

      Uncommented the <HostPort> definition for port 443 in /conf/VHost.xml

      Set the value SSLConfig/KeyStorePath

      <KeyStorePath>${com.wowza.wms.context.VHostConfigH ome}/conf/www.prudhub.com.cert</KeyStorePath>
      <KeyStorePassword>changeit</KeyStorePassword>

      added
      <Property>
      <Name>cupertinoEncryptionBaseURL</Name>
      <Value>http://192.168.1.7:1935</Value>
      </Property>
      to /conf/ipad_dailies/Application.xml

      Rebooted server

      Yet when I goto
      https://www.prudhub.com:1935/ipad_da.../playlist.m3u8 in Safari
      I get
      Safari can’t open the page “https://www.prudhub.com:1935/ipad_da...playlist.m3u8” because Safari can’t connect to the server “www.prudhub.com”.

      and the wowza log reads
      2010-11-24 16:36:01 PST disconnect session INFO 200 877797065 - _defaultVHost_ - - 20.586 [any] 1935 - 127.0.0.1 rtmp - unknown 877797065 58 18 - - - - - - - - - - - - - - -

      Can you tell me where I'm going wrong?
    1. charlie -
      Be sure port 443 is open on your firewall. To test that certificate is working, open a web browser and enter the address:

      https://[wowza-ip-address]

      You should get back the Wowza version number and it should be secured with SSL. You need to get this working first.

      Then the problem with what you tried above is:

      1. The cupertinoEncryptionBaseURL should be:
        Code:
        <Property>
        <Name>cupertinoEncryptionBaseURL</Name>
        <Value>https://192.168.1.7</Value>
        </Property>
      2. If you are only using SSL for key delivery then there is no need to use it for the playlist and chunk delivery. So the playlist URL should be:
        Code:
        http://www.prudhub.com:1935/ipad_dailies/mp4:sample.mp4/playlist.m3u8
      3. The error indicates that www.prudhub.com is not properly resolving to an IP address


      Charlie
    1. solasilor -
      Thanks for your response.

      I'm still having problems getting this to work.

      Now I've created a wowza server on my win7 box.
      I've created a self-signed cert successfully.
      I've opened the 443 port in Vhost.xml.
      In Safari I goto https://localhost and I get back 'Wowza Media Server 2 Developer 2.1.2 build24878'
      There are no entries in the log for this https connection

      However, when I go to: https://localhost/ipad_dailies/mp4:s.../playlist.m3u8
      I still get back 'Wowza Media Server 2 Developer 2.1.2 build24878' (it is not going past https://localhost)
      Again there are no entries in the log for this https connection

      When I go to: htts://localhost/ipad_dailies/mp4:sample.mp4/playlist.m3u8 (unsecured)
      It just wants to download the playlist.m3u8 file.

      I guess I'm thoroughly confused as to how to get this working. Any light you can shed on what I'm doing wrong would be greatly appreciated.
    1. solasilor -
      Thanks for your response.

      I'm still having problems getting this to work with an iPad.

      OK. I've gone through your tutorial entitled: How to play a video on demand file and am able to get the examples to work.

      Then I created and installed a self signed cert and modified the Application.xml and Vhost.xml (per this threads instructions)

      I can now get a secure rtmps stream but it chokes on the https stream: https://75.92.125.71:1935/vod/mp4:sa.../playlist.m3u8 (Note this server is not always up). Can you please offer a solution?
    1. charlie -
      Self-signed certificates will rarely work properly. Many browsers/players will just refuse the requests because the certificate is not signed.

      Charlie
    1. kkaushik -
      Quote Originally Posted by charlie View Post
      Self-signed certificates will rarely work properly. Many browsers/players will just refuse the requests because the certificate is not signed.

      Charlie
      Charlie,I have the valid certifcate installled on the wowza server and also i have replaced the property in config file for secure key exchange.
      <Property>
      <Name>cupertinoEncryptionBaseURL</Name>
      <Value>https://dev-dmgstream.spe.sony.com</Value>
      </Property>


      However when i try to play video using the https link from ipad it gives error that path not found.
      https://dev-dmgstream.spe.sony.com/t.../playlist.m3u8

      When i look into the log i see that it tries to play the chunks through http and port 80. When i assign port 80 to Wowza it starts playing.
      Can you tell me a way in which we can still play the https link without assigning the port 80 to wowza.

      Kunal
    1. rrlanham -
      same as ticket 7818
    1. kkaushik -
      how to see the ticket# 7818. Can somebody please paste the link to that ticket here?
    1. rrlanham -
      Add AES internal to Module Collection:

      http://www.wowzamedia.com/forums/con...ule-Collection

      Richard
    1. snehal -
      Hi,

      I'm facing the problem for https VOD HLS streaming
      Able to generate the certificate on both Wowza and client
      Client is able to access the server through https://<<wowza_ip>>
      but not able to play apple hls stream https://<<wowza_ip>>:1935/vod/sample.mp4/playlist.m3u8

      Application.xml file in /conf/vod is configured as below

      <Property>
      <Name>cupertinoEncryptionBaseURL</Name>
      <Value>https://10.5.21.111</Value>
      </Property>

      Immediate reply is appreciated

      Thanks,
      Snehal
    1. rrlanham -
      Snehal,

      I think you have to add port 1935 to cupertinoEncryptionBaseURL

      Code:
      <Property>
      <Name>cupertinoEncryptionBaseURL</Name>
      <Value>https://10.5.21.111:1935</Value>
      </Property>
      Or, enable port 80 and serve Cupertino streams using default http port.

      Richard
    1. rrlanham -
      Actually, the port should be whatever you have configured SSL on. So if it is 443, make sure that is configured in VHost.xml and use that in playlist.m3u8 URLs

      Richard
    1. snehal -
      Thanks Richard, it worked for me with the previous configuration
      one query - whether all .ts files in playlist.m3u8 will be served over Https or not
      Also does it support URL hashing in Wowza, here it talks about shared secret hash for encryption key. so does it hash encryption key or the streaming url
      Immediate reply is appreciated
    1. rrlanham -
      AES encrypts the .ts chunks, SSL encrypts the playback url querystring that contains the encryption key.

      You don't need to serve the .ts chunks over SSL. You could, but it is more overhead all around and unnecessary, they are already encrypted.

      Richard
    1. snehal -
      Ok thanks
      I understand this more secure as chunks are encrypted & it is over SSL
      1. But what about URL hashing using MD5, does wowza support it?
      2. This mechanism also uses MD5 right? so what is the purpose over here
    1. aynajus -
      Hi

      I followed all the steps and it worked. My ipod can play content.

      So, How do I know. Another can't access my content.

      please tell my, How do I test it ?

      thanks
    1. rrlanham -
      Follow these steps:

      http://www.wowza.com/forums/content....AES-Encryption

      Richard
    1. StreamingMans -
      Please help...
      I am use this example to apply to my work and it work well for single wowza server . if i want to apply
      this example to Origin - Edge structure, please guide and explain me how to implement this case?
    1. rrlanham -
      I'm not sure about the AES external method, but the AES internal method has properties concerning edge delivery

      http://www.wowza.com/forums/content....nternal-method)

      Richard
    1. StreamingMans -
      Hello Richard , Thanks for your response.


      I have some problem , when i use media stream validator tool to validate live stream that
      implement with this example and it response some message below
      .......
      ERROR: Media segment is incorrectly encrypted.
      7: media_3954.ts?wowzasessionid=668609846..........
      ERROR: Media segment is incorrectly encrypted.
      9: media_3955.ts?wowzasessionid=668609846..........
      ERROR: Media segment is incorrectly encrypted.
      11: media_3956.ts?wowzasessionid=668609846..........
      ......
      WARNING: Insecure encryption key URI.
      5: #EXT-X-KEY:METHOD=AES-128,URI="http://[mycompany-ip]:1935/live/_definst_/[stream_name]/key.m3u8key?wowzasessionid=668609846........

      please guide me for this problem