多协议、性能稳定、丰富API的流媒体服务器软件
如何在内部采用AES-128对HLS的TS流进行加密?
下面介绍了如何在Wowza Stream Engine 4内部采用AES-128对HLS流媒体中的TS流进行加密(包括直播业务和VOD点播业务)。

关于Http Live Streaming的详细技术规范,请参考互联网工程小组的draft-pantos-http-live-streaming-05 specification

在Wowza Stream Engine 4内部使用AES-128加密时,所有的内容切片和密钥都是由Wowza Stream Engine 4提供给播放器的。 此外,还有一个在Wowza外部使用AES-128加密的方法,在这个方式下,密钥是由一个外部的WEB服务器提供给播放器的。要了解更多,请阅读如何在Wowza外部采用AES-128对HLS的TS流进行加密

要配置一个面向iOS设备提供hls流媒体的应用。请阅读Wowza Streaming Engine 4快速上手

下面的模块是一个如何在内部采用AES-128对HLS的TS流进行加密的框架。下面的方法控制着加密流程:

  • The onHTTPCupertinoEncryptionKeyRequest 方法用于对获取密钥的请求进行访问控制。这个方法内的代码还没有完全写完,需要补充一些保护密钥的业务逻辑。你可以在这里添加一些诸如检查URL查询参数、cookies或者其它request中的参数来控制对密钥的访问。

  • The onHTTPCupertinoEncryptionKeyCreateLive 方法是在实时流每次开始推送(或拉)时被调用(就一次)。在这里你可以控制用于加密的密钥。你可以看到在这个方法中我们用共享密钥、当前流的上下文环境(应用名、应用实例名以及stream name)的组合创建了一个唯一的MD5摘要字符串。这将保证生成的密钥是唯一的。

  • The onHTTPCupertinoEncryptionKeyCreateVOD 方法在每一个VOD连接请求时被调用。在这里,我们用共享密钥、sessoin id和当前流的上下文环境(应用名、应用实例名以及stream name)的组合为每一个session创建了密钥。


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+":"+(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));
	}

}
一个编译好的版本已经被包含在Wowza 模块集合中。下载并解开压缩包,将包中的/lib/wms-plugin-collection.jar 文件拷贝到Wowza Media Server 的安装路径的[install-dir]/lib中,然后重启服务器。

注意: 这个模块仅仅是对HLS流进行加密。如果你需要更好的控制整个Session,你就必须自己完成代码并使用Wowza IDE编译它。

接下来, 在[install-dir]/conf/[application]/Application.xml文件中的<Modules>列表的最后添加下面的模块:
<Module>
	<Name>ModuleEncryptionHandlerCupertinoStreaming</Name>
	<Description>ModuleEncryptionHandlerCupertinoStreaming</Description>
	<Class>com.wowza.wms.plugin.collection.module.ModuleEncryptionHandlerCupertinoStreaming</Class>
</Module>
将下面的属性添加到[install-dir]/conf/[application]/Application.xml文件最下面的的应用级别的<Properties>中:
<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>
将下面的属性添加到 [install-dir]/conf/[application]/Application.xml文件的HTTPStreamer/Properties中:
<Property>
	<Name>cupertinoEnableOnEncKey</Name>
	<Value>true</Value>
	<Type>Boolean</Type>
</Property>
  • cupertinoEncryptionBaseURL 是获取密钥的请求的base-URL。如果你配置使用SSL连接,请确定在URL中保留端口号(:1935), 并将URL的前缀从http://修改为https://, 另外最好使用域名而不是IP地址来指定Wowza Media Server。

  • cupertinoEncryptionSharedSecret: 是共享密钥,它被用于采用Hash算法生成唯一加密密钥时的输入。上面的例子展示了它是被如何使用的。这个例子中的共享密钥为 1ghtY6D3Wgn.

  • cupertinoEncryptionLiveRepeaterSharedSecret:是采用中心/边缘架构(origin/edge)时,将加密要由中心服务器向边缘服务器传递时采用的共享密钥。当配置边缘服务器提供直播服务时,请确定在边缘服务器上将cupertinoEncryptionLiveRepeaterSharedSecret 设置为相同的值。这个例子中这个值为3Er5sWl09xfE).

  • cupertinoEnableOnEncKey 打开Wowza的内部加密密码传送功能。


如果你在Wowza nDVR使用这个机制,要设置的属性参数略有不同。请在[install-dir]/conf/[application]/Application.xml文件的DVR <Properties>中添加以下属性:
<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>
[install-dir]/conf/[application]/Application.xml文件的HTTPStreamer/Properties中添加下面的属性:
<Property>
	<Name>cupertinoEnableOnEncKey</Name>
	<Value>true</Value>
	<Type>Boolean</Type>
</Property>
注意: 关于对AES加密的测试,请阅读如何测试Apple HLS流媒体的AES加密功能

注意: 对于加密密钥的传送,最好在[install-dir]/conf/VHost.xml文件的<HostPort>中配置使用SSL加密。这将保护加密要在传输中不被拦截。请阅读Wowza技术文档中的Wowza Streaming Engine用户使用手册了解更多关于SSL的配置。如果你配置在服务端口上使用SSL加密,请确定去掉if (!req.isSecure())的注释,并在onHTTPCupertinoEncryptionKeyRequest方法中检查以确定使用了SSL加密传输。