Secure CMAF MPEG-DASH streams using Common Encryption in Wowza Streaming Engine

Wowza Streaming Engine™ media server software can protect live CMAF-packetized MPEG-DASH streams using the Common Encryption (CENC) standard. Since CENC is DRM system-agnostic, it allows the same CENC-encrypted content to be decrypted and played by any client device that interfaces with a DRM system that can serve the associated CENC key information. CENC-encrypted CMAF MPEG-DASH streams also allow carriage of proprietary DRM system-specific data, which allows a client device to transact with the particular DRM system they are integrated with for key retrieval. Support for this proprietary DRM data requires some DRM-specific functionality to be added to the server internals for each DRM system.

Wowza Streaming Engine supports on-the-fly CMAF MPEG-DASH CENC encryption for live content via the PlayReady and Widevine DRM systems.

Notes:
  • Wowza Streaming Engine 4.8.0 or later is required.
  • HLS playback of CENC-encrypted CMAF-packetized streams isn't supported at this time.

This article assumes familiarity with CENC and DRM technologies. Wowza Streaming Engine doesn't function as a CENC key server. It instead can be used to encrypt and deliver content on the fly.

Wowza DRM


Wowza Streaming Engine software includes Wowza DRM, a feature that enables direct integration with third-party CENC PlayReady DRM vendors. When using this option, you'll be using the PlayReady key server provided by the third-party vendor. Wowza DRM includes a direct integration with the third-party key server to acquire the encryption keys and license URLs needed to protect your content. For more detailed information, see About digital rights management and Wowza Streaming Engine.

On-the-fly DASH CENC encryption using key files


Live streams can be CENC-PlayReady or CENC-Widevine encrypted on the fly using key files. When using this option, you must provide a PlayReady or Widevine key server. Key files are text files that are located in the [install-dir]/keys folder that match the stream name of the stream you're playing with the addition of a .key extension.

For example, if you want to protect the live stream myStream, you would create a key file with the path [install-dir]/keys/myStream.key.

The naming is similar for live CMAF MPEG-DASH adaptive bitrate streams referenced in SMIL files. For example, if you want to protect all of the streams in an adaptive group defined in the SMIL file sample.smil, create a key file with the path [install-dir]/keys/sample.smil.key.

In this case, all of the streams in the adaptive group will be encrypted with the same key.

The key file is a text file. For PlayReady DRM, it has the following (or similar) content:

cmafstreaming-cenc-key-id: F6005DCF-7F93-4B8E-85C7-F908840DA059
cmafstreaming-cenc-content-key: Tc2cQBPC/paTkftvaITCSQ==
cmafstreaming-cenc-algorithm: AESCTR
cmafstreaming-cenc-keyserver-playready: true
cmafstreaming-cenc-keyserver-playready-system-id: 9a04f079-9840-4286-ab92-e65be0885f95
cmafstreaming-cenc-keyserver-playready-license-url: http://myplayreadyserver.com/authenticate.aspx
cmafstreaming-cenc-keyserver-playready-checksum: vbxgstjfSQY=

For Widevine DRM, the key file has the following (or similar) content:

cmafstreaming-cenc-key-id: 0294B959-9D75-5DE2-BBF0-FDCA3FA5EAB7
cmafstreaming-cenc-content-key: O9ovQDRMfe9hQie5wPA+Jg==
cmafstreaming-cenc-algorithm: AESCTR
cmafstreaming-cenc-keyserver-widevine: true
cmafstreaming-cenc-keyserver-widevine-system-id: edef8ba9-79d6-4ace-a3c8-27dcd51d21ed
cmafstreaming-cenc-keyserver-widevine-pssh-data: CAESEAKUuVmddV3iu/D9yj+l6rcaDXdpZGV2aW5lX3Rlc3QiEGZrajNsamFTZGZhbGtyM2oqAlNEMgA=

For a combined PlayReady and Widevine DRM configuration, the key file has the following (or similar) content:

cmafstreaming-cenc-key-id: 0294B959-9D75-5DE2-BBF0-FDCA3FA5EAB7
cmafstreaming-cenc-content-key: O9ovQDRMfe9hQie5wPA+Jg==
cmafstreaming-cenc-algorithm: AESCTR
cmafstreaming-cenc-keyserver-widevine: true
cmafstreaming-cenc-keyserver-widevine-system-id: edef8ba9-79d6-4ace-a3c8-27dcd51d21ed
cmafstreaming-cenc-keyserver-widevine-pssh-data: CAESEAKUuVmddV3iu/D9yj+l6rcaDXdpZGV2aW5lX3Rlc3QiEGZrajNsamFTZGZhbGtyM2oqAlNEMgA=
cmafstreaming-cenc-keyserver-playready: true
cmafstreaming-cenc-keyserver-playready-system-id: 9a04f079-9840-4286-ab92-e65be0885f95
cmafstreaming-cenc-keyserver-playready-license-url: http://myplayreadyserver.com/authenticate.aspx
cmafstreaming-cenc-keyserver-playready-checksum: vbxgstjfSQY=
 
Note: The above keys are fabricated and won't work properly to encrypt/decrypt content. Follow the appropriate PlayReady or Widevine documentation to generate a proper set of keys for testing. For example, for PlayReady you must generate keys using the PlayReady SDK.

The following is a description of each of the items in the key file:

  • cmafstreaming-cenc-key-id – The key ID for this asset.
  • cmafstreaming-cenc-content-key – The actual content encryption key (128-bit key), Base64 encoded.
  • cmafstreaming-cenc-algorithm – The encryption algorithm. The only supported value is AESCTR.
  • cmafstreaming-cenc-pssh-version – The PSSH box version for use with Encrypted Media Extensions (EME). The only supported values are 0 and 1.
  • cmafstreaming-cenc-pssh-flags –  The PSSH flags for use with EME. The only supported value is 0.
  • cmafstreaming-cenc-keyserver-playready – Must be set to true to enable CENC-PlayReady encryption.
  • cmafstreaming-cenc-keyserver-playready-system-id – This optional parameter is the DRM system-specific identifier for PlayReady DRM and must be set to 9a04f079-9840-4286-ab92-e65be0885f95 if provided. This is the value used by the server by default when the parameter isn't provided.
  • cmafstreaming-cenc-keyserver-playready-license-url – The PlayReady license URL used by the player to authenticate the player and retrieve the decryption key needed for playback.
  • cmafstreaming-cenc-keyserver-playready-checksum – A checksum of the key ID that's needed to authenticate the player.
  • cmafstreaming-cenc-keyserver-widevine – Must be set to "true" to enable CENC-Widevine encryption.
  • cmafstreaming-cenc-keyserver-widevine-system-id – This optional parameter is the DRM system-specific identifier for Widevine DRM, and must be set to edef8ba9-79d6-4ace-a3c8-27dcd51d21ed if provided. This is the value used by the server by default when the parameter isn't provided.
  • cmafstreaming-cenc-keyserver-widevine-pssh-data – Base-64-encoded protection system specific header data for the content.

With a key file in place, the associated CMAF MPEG-DASH stream is encrypted using CENC-PlayReady or CENC-Widevine encryption before being delivered. 

When CMAF packetization begins for live streaming, you'll see a similar message in the log files:

LiveStreamPacketizerCmaf.init[live/_definst_/myStream]: Encrypt CMAF: key-id:F6005DCF-7F93-4B8E-85C7-F977740DA059

It's best to get unencrypted CMAF MPEG-DASH live streaming working before adding encryption. Then, follow these instructions to protect the stream. Another debugging method to ensure the CMAF MPEG-DASH stream is CENC-PlayReady or CENC-Widevine protected is to download the DASH MPD file directly to the browser window by entering the MPD URL into the browser address field. If the stream is PlayReady-encrypted, when you look at any audio or video AdaptionSet, you should see a <ContentProtection> element that looks like the following:

<ContentProtection schemeIdUri="urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95" value="Microsoft PlayReady">

If the stream is Widevine-encrypted, then the <ContentProtection> element should look like the following:

<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" value="Widevine">

On-the-fly CENC-PlayReady encryption using server-side API


Similar to the key files described above, MPEG-DASH streams can be protected using key data passed to Wowza Streaming Engine using the server-side API. There is a method that can be added to a server-side module to control CMAF MPEG-DASH CENC-PlayReady encryption for live streams.

public abstract void onHTTPCmafEncryptionKeyLiveSegment(ILiveStreamPacketizer liveStreamPacketizer, String streamName, CencInfo cencInfo, long segmentId, int contentType)
{
  // set CENC key info
  cencInfo.setAlgorithm(CencInfo.ALGORITHMID_CTR);
  cencInfo.setKID("F6005DCF-7F93-4B8E-85C7-F977740DA059");
  cencInfo.setEncKeyBytes(Base64.decode("Tc2cQBPC/paTkftvaITCSQ=="));

  // add PlayReady DRM info
  PlayReadyKeyInfo playReadyKeyInfo = new PlayReadyKeyInfo();
  playReadyKeyInfo.setLicenseURL("http://myplayreadyserver.com/authenticate.aspx");
  playReadyKeyInfo.setChecksum(Base64.decode("vbxgstjfSQY="));
  playReadyKeyInfo.setKeyId(BufferUtils.decodeHexString("F6005DCF-7F93-4B8E-85C7-F977740DA059".replace("-", "")));
  playReadyKeyInfo.setContentKey(Base64.decode("Tc2cQBPC/paTkftvaITCSQ=="));
  CencDRMInfoPlayready prInfo = new CencDRMInfoPlayready();
  prInfo.setPlayReadyKeyInfo(playReadyKeyInfo);
  cencInfo.addDRM("myPlayReadyDRM:9a04f079-9840-4286-ab92-e65be0885f95", prInfo);
}

On-the-fly CENC-Widevine encryption using server-side API


Similar to the key files described above, CMAF MPEG-DASH streams can be protected using key data passed to Wowza Streaming Engine using the server-side API. There is a method that can be added to a server-side module to control CMAF MPEG-DASH CENC-Widevine encryption for live streams.

public abstract void onHTTPCmafEncryptionKeyLiveSegment(ILiveStreamPacketizer liveStreamPacketizer, String streamName, CencInfo cencInfo, long segmentId, int contentType)
{
  // set CENC key info
  cencInfo.setAlgorithm(CencInfo.ALGORITHMID_CTR);
  cencInfo.setKID(Base64.decode("ApS5WZ11XeK78P3KP6Xqtw=="));
  cencInfo.setEncKeyBytes(Base64.decode("O9ovQDRMfe9hQie5wPA+Jg=="));

  // add Widevine DRM info
  CencDRMInfoWidevine drmInfo = new CencDRMInfoWidevine();
  drmInfo.setSystemId("edef8ba9-79d6-4ace-a3c8-27dcd51d21ed");
  drmInfo.setPsshData(Base64.decode("CAESEAKUuVmddV3iu/D9yj+l6rcaDXdpZGV2aW5lX3Rlc3QiEGZrajNsamFTZGZhbGtyM2oqAlNEMgA="));
  cencInfo.addDRM("myWidevineDRM:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", drmInfo);
}

On-the-fly CENC-Multi-DRM encryption using server-side API


CMAF MPEG-DASH streams can be protected using key data supported by multiple DRM systems, which can be passed to Wowza Streaming Engine using the server-side API. There is a method that can be added to a server-side module to control live stream CMAF MPEG-DASH CENC encryption for multiple DRM systems.

public abstract void onHTTPCmafEncryptionKeyLiveSegment(ILiveStreamPacketizer liveStreamPacketizer, String streamName, CencInfo cencInfo, long segmentId, int contentType)
{
  // set CENC key info
  cencInfo.setAlgorithm(CencInfo.ALGORITHMID_CTR);
  cencInfo.setKID(Base64.decode("ApS5WZ11XeK78P3KP6Xqtw=="));
  cencInfo.setEncKeyBytes(Base64.decode("O9ovQDRMfe9hQie5wPA+Jg=="));

  // add DRM info for first DRM system (Widevine in this case)
  CencDRMInfoWidevine drmInfo = new CencDRMInfoWidevine();
  drmInfo.setSystemId("edef8ba9-79d6-4ace-a3c8-27dcd51d21ed");
  drmInfo.setPsshData(Base64.decode("CAESEAKUuVmddV3iu/D9yj+l6rcaDXdpZGV2aW5lX3Rlc3QiEGZrajNsamFTZGZhbGtyM2oqAlNEMgA="));
  cencInfo.addDRM("myWidevineDRM:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", drmInfo);

  // add DRM info for second DRM system (PlayReady in this case)
  PlayReadyKeyInfo playReadyKeyInfo = new PlayReadyKeyInfo();
  playReadyKeyInfo.setLicenseURL("http://myplayreadyserver.com/authenticate.aspx");
  playReadyKeyInfo.setChecksum(Base64.decode("vbxgstjfSQY="));
  playReadyKeyInfo.setKeyId(BufferUtils.decodeHexString("F6005DCF-7F93-4B8E-85C7-F977740DA059".replace("-", "")));
  playReadyKeyInfo.setContentKey(Base64.decode("Tc2cQBPC/paTkftvaITCSQ=="));
  CencDRMInfoPlayready prInfo = new CencDRMInfoPlayready();
  prInfo.setPlayReadyKeyInfo(playReadyKeyInfo);
  cencInfo.addDRM("myPlayReadyDRM:9a04f079-9840-4286-ab92-e65be0885f95", prInfo);
}

onHTTPCmafEncryptionKeyLiveSegment()

This API is initially called at stream initialization when packetization starts (not session-based), at which point the segmentId field is set to -1.

The module should populate the CencInfo data (and internal PlayReadyKeyInfo data for CENC-PlayReady) in a manner similar to the example above.

onHTTPCmafEncryptionKeyLiveSegment() is subsequently called again each time a CMAF segment is about to be created, and the segmentId field is set to the respective segment number.

In the initial call where segmentId is -1, a call to cencInfo.getKID() should return null. On all ensuing calls to onHTTPCmafEncryptionKeyLiveSegment() for that session, cencInfo.getKID() returns the KID value that was last provided to allow the module to verify or replace it with a new key (the CENC data is remembered across each call for that stream). If the module doesn't change the key on the ensuing calls, then all live client connections get the same set of segments encrypted with the same key. The module could optionally provide encryption info on periodic calls to emulate key rotation.

If you don't provide key information when the method is called, then the stream won't be encrypted.

Testing CMAF-packetized MPEG-DASH CENC-encrypted playback


The following players can play either our PlayReady-encrypted or Widevine-encrypted CMAF-packetized DASH streams using the static key and protection system specific header (pssh) information provided in the examples above, using either key files or module APIs:

Notes


  • You can resolve all of the classes used by the examples in this article by using the following imports:
     
    import com.wowza.wms.module.ModuleBase;
    import com.wowza.util.*;
    import com.wowza.wms.drm.cenc.*;
    import com.wowza.wms.drm.playready.*;
    import com.wowza.wms.httpstreamer.mpegdashstreaming.httpstreamer.HTTPStreamerSessionMPEGDash;
    import com.wowza.wms.stream.livepacketizer.ILiveStreamPacketizer;
  • There are multiple ways to set the KID in the CencInfo class:
     
    // if KID is base64-encoded:
    cencInfo.setKID(Base64.decode("ApS5WZ11XeK78P3KP6Xqtw=="));
    // if KID is in byte buffer form:
    cencInfo.setKID(<kidByteBuffer>);
    // if KID is uuid form:
    cencInfo.setKID("F6005DCF-7F93-4B8E-85C7-F977740DA059");
  • There are multiple ways to set the encryption key in the CencInfo class:
     
    // if key is base64-encoded:
    cencInfo.setEncKeyBytes(Base64.decode("O9ovQDRMfe9hQie5wPA+Jg=="));
    // if key is in byte buffer form:
    cencInfo.setEncKeyBytes(<keyByteBuffer>)
  • When using this option, you must provide your own PlayReady or Widevine key server.
  • Key rotation using PlayReady Embedded License Stores (ELS) is not supported.
  • Providing CMAF CENC key information through a key file for a given live stream will result in the associated module API above to never be called for that stream.