An End-to-End Prototype of PlayReady Protection with ACS Authentication and ACS Token Authorization

在 十一月 17, 2014 上貼文

Principal Program Manager, Consumer & Media


This end-to-end prototype was in response to the requests from customers in Asia and Europe. Azure Media Services Content Protection contains the following:
  • PlayReady license delivery service;
  • AES 128 clear key delivery service;
  • Dynamic PlayReady protection or AES encryption during delivery time;
  • Static PlayReady protection or AES encryption in media processing workflow.
It is notable that the dynamic PlayReady protection feature in Azure Media Services makes “one-click DRM” a reality: customers or partners do not need to go through any of the following steps which are required in "traditional DRM":
  1. Secure PlayReady server license,
  2. Standing up PlayReady license server farm,
  3. Going through the PlayReady packaging workflow to protect an asset.
You can protect or un-protect an asset with a "single click" and is done at the speed of a web page refresh. Typically a DRM solution involves multiple moving parts and the following blog provides an overview of the building blocks in a DRM solution: Solution Design Considerations in Using PlayReady License Delivery Service of Azure Media Services. A complete end-to-end prototype would be desirable, which is the goal of this effort.  

The End-to-End Prototype

Design and Functionality

The goal of this effort is to provide an end-to-end prototype covering the following
  • PlayReady dynamic protection (or static protection) with Token Restriction for an asset in AMS;
  • Azure Media Services license delivery service for delivering PlayReady licenses;
  • Azure ACS (Microsoft Azure Active Directory Access Control) as an STS to issue SWT authorization tokens;
  • A Silverlight player
    1. gets authenticated by ACS Service Identity,
    2. gets authorization token from ACS,
    3. acquires PlayReady licenses from AMS license delivery service with ACS token and
    4. downloads Smooth Streaming or MPEG-DASH asset for subsequent video playback.
I chose to use a Silverlight player simply for the following reasons:
  1. To make the player accessible by popular browsers such as IE, Chrome and Firefox,
  2. Smooth Streaming Client v2.5 comes with PlayReady client support which does not require PlayReady client licensing,
  3. Smooth Streaming Client v2.5 also supports MPEG-DASH. This end-to-end prototype does cover MPEG-DASH in addition to smooth streaming.
  The design of this end-to-end prototype is illustrated by the following diagram. It is a specialization of the general DRM solution diagram discussed in the author’s blog Solution Design Considerations in Using PlayReady License Delivery Service of Azure Media Services.   Blog_Secure_Delivery_Prototype   The end-to-end prototype is hosted in Azure and Azure Media Services. Below is the information related to the prototype.
<TokenRestrictionTemplate xmlns:i="" xmlns="">
    <TokenVerificationKey i:type="SymmetricVerificationKey">
      <KeyValue>(portion deleted) Yw1z2wxh6ZkX4tRl/WVhBTvM6T/vUo=</KeyValue>
  <PrimaryVerificationKey i:type="SymmetricVerificationKey">
    <KeyValue>(portion deleted) A8RlVMrPNhukYBF2sW04UMpuD8bw=</KeyValue>
  <RequiredClaims />

How to Run It?

Here are the simple steps to run this prototype using the test player:
  1. Browse to the player
  2. Check “Add URL” checkmark so that you can enter URLs;
  3. Check “Add AuthN ACS Token” checkmark so that ACS authorization token will be requested first and will be used in PlayReady license acquisition from the license acquisition URL specified in LA_URL text box;
  4. Copy either the smooth streaming asset URL or the MPEG-DASH asset URL and paste it into the SRC_URL text box. Then, press the “Add to Playlist” button to play.
  Player Upon button click, the player will then:
  1. Download the client manifest of the asset under PlayReady dynamic protection;
  2. Get authenticated by the ACS 2.0 namespace via its Service Identity;
  3. Request authorization token from ACS;
  4. Request PlayReady license from LA_URL with the ACS token;
  5. Decrypt, check PlayReady restriction and right, then playback the video.
Of course, if you uncheck the “Add AuthN ACS Token” checkmark and click the button, it will fail at PlayReady license acquisition due to missing authorization token from ACS namespace.  

The Implementation

The implementation includes the following:
  1. Configure PlayReady dynamic protection for an smooth streaming asset using Azure Media Services .NET API;
    • Generate content key ID and content key;
    • Configure key delivery service;
    • Configure dynamic PlayReady protection via asset delivery policy;
    • Publish the asset.
  2. Set up an Azure ACS 2.0 namespace to authenticate the player client and issue authorization tokens;
  3. Develop a Silverlight player which handles authentication, authorization, license acquisition and video playback.


Content Key Generation

There are various approaches in generating content key IDs and content keys. For a detailed discussion, please see the author’s blog (Key Generation and Management section). For example, there are the following ways:
string keySeedB64, contentKeyB64;
Guid keyId = Guid.NewGuid();
//Guid keyId = new Guid("09a2212a-a803-4989-9a6e-6cd2e69500e7");
//Method 1: Without using key seed, generete content key directly
//contentKeyB64 = GeneratePlayReadyContentKey();
//Method 2: With a given key seed and generated key ID (Key Identifiers are unique in the system and there can only be one key with a given Guid within a cluster (even across accounts for now although that may change to be account scoped in the future).  If you try to submit a protection job with a keyId that already exists but a different key value that will cause the PlayReady protection job to fail (the same keyId and keyValue is okay). 
keySeedB64 = "XVBovsmzhP9gRIZxWfFta3VVRPzVEWmJsazEJ46I";
contentKeyB64 = GetPlayReadyContentKeyFromKeyIdKeySeed(keyId.ToString(), keySeedB64);
//Method 3: With a randomly generated key seed, create content key from the key ID and key seed
//keySeedB64 = GeneratePlayReadyKeySeed();
//contentKeyB64 = GetPlayReadyContentKeyFromKeyIdKeySeed(keyId.ToString(), keySeedB64);
//Method 4: Reuse an existing key ID (only once, for test)
//keyId = new Guid("a7586184-40ff-4047-9edd-6a8273ac50fc");
//keySeedB64 = "XVBovsmzhP9gRIZxWfFta3VVRPzVEWmJsazEJ46I";
Console.WriteLine(string.Format("STEP 1: Key ID = {0}, Content Key = {1}, Key Seed = {2}", contentKeyB64, keyId.ToString(), keySeedB64));
The following utility methods are used in generating key ID and key seed:
public static byte[] GenerateCryptographicallyStrongRandomBytes(int length)
    byte[] bytes = new byte[length];
    //This type implements the IDisposable interface. When you have finished using the type, you should dispose of it either directly or indirectly. To dispose of the type directly, call its Dispose method in a try/catch block. To dispose of it indirectly, use a language construct such as using (in C#) 
    using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider())
    return bytes;
//generate a PlayReady content key: cryptographically strong random byte[16]
public static string GeneratePlayReadyContentKey()
    byte[] bytes = GenerateCryptographicallyStrongRandomBytes(16);
    return Convert.ToBase64String(bytes);
public static string GeneratePlayReadyKeySeed()
    byte[] bytes = GenerateCryptographicallyStrongRandomBytes(30);   //30 for key seed:
    return Convert.ToBase64String(bytes);
public static string GenerateSymmetricHashKey()
    byte[] bytes = GenerateCryptographicallyStrongRandomBytes(32);  
    return Convert.ToBase64String(bytes);
//This API works the same as AESContentKey constructor in PlayReady Server SDK 
public static string GetPlayReadyContentKeyFromKeyIdKeySeed(string keyIdString, string keySeedB64)
    Guid keyId = new Guid(keyIdString);
    byte[] keySeed = Convert.FromBase64String(keySeedB64);

    byte[] contentKey = CommonEncryption.GeneratePlayReadyContentKey(keySeed, keyId);

    string contentKeyB64 = Convert.ToBase64String(contentKey);

    return contentKeyB64;

Code for Azure Media Services

On Azure Media Services side, suppose you have an unprotected smooth streaming asset. The steps are
  1. Remove any locators this asset might have. This is important since the next step cannot proceed if locator exists.
  2. Remove any asset delivery policy it might have;
  3. Configure license delivery service;
  4. Configure dynamic PlayReady protection with token restriction;
  5. Publish the asset.
The code for setting up PlayReady key delivery service and dynamic protection of the input asset can be found in GitHub Azure/azure-media-services-samples/.  

ACS Setup

Please see Mingfei’s blog How to configure ACS with Media Services key services for the steps of configuring an Azure ACS v 2.0 namespace. In general there are two authentications:
  1. End users are authenticated by an Identity Provider to gain access to the web application hosting the player, the so-called Relying Party. In this prototype, we have chosen to leave the web application open without the need for user authentication. Therefore there is no need of an Identity Provider for the web application.
  2. Client/autonomous applications are authenticated by ACS 2.0 namespace to gain access and acquire tokens. For this purpose, we specify a Service Identity to authenticate directly with ACS instead of using an Identity Provider. This Service Identity is then used by client to get authenticated by ACS in order to request authorization token from the ACS namespace.
  Below is an example of a token issued by this ACS 2.0 namespace:
  NOTE: Please make sure that the same (primary) symmetric hash key used in ACS 2.0 namespace is also used in configuring PlayReady dynamic protection. Specifically, when you programmatically set up PlayReady dynamic protection, you need to create Restriction Requirements used in IContentKeyAuthorizationPolicy, as shown below.
public static string CreateRestrictionRequirements()
    string primarySymmetricKey   = System.Configuration.ConfigurationManager.AppSettings["PrimarySymmetricKey"];
    string secondarySymmetricKey = System.Configuration.ConfigurationManager.AppSettings["SecondarySymmetricKey"];
    string scope                 = System.Configuration.ConfigurationManager.AppSettings["AcsScope"];
    string issuer                = System.Configuration.ConfigurationManager.AppSettings["AcsIssuer"];

    TokenRestrictionTemplate objTokenRestrictionTemplate = new TokenRestrictionTemplate();

    objTokenRestrictionTemplate.PrimaryVerificationKey = new SymmetricVerificationKey(Convert.FromBase64String(primarySymmetricKey));
    objTokenRestrictionTemplate.AlternateVerificationKeys.Add(new SymmetricVerificationKey(Convert.FromBase64String(secondarySymmetricKey)));
    objTokenRestrictionTemplate.Audience               = new Uri(scope);
    objTokenRestrictionTemplate.Issuer                 = new Uri(issuer);

    return TokenRestrictionTemplateSerializer.Serialize(objTokenRestrictionTemplate);
  The primarySymmetricKey variable should contain the same symmetric hash key string as obtained from ACS 2.0 Management Portal as shown below:   SymmetricHashKey When you create Service Identity in ACS namespace, you may choose either Password or Symmetric Key credential types. The token request code has been enhanced to support both cases.  

Code on Client Side

The client side code for requesting authorization token from ACS and the custom license acquirer inside Silverlight can be found in GitHub Azure/azure-media-services-samples. Player application performs the following:
  1. First request the manifest of a smooth streaming asset under PlayReady protection and sees the protection header in the manifest;
  2. In order to request PlayReady license, the player needs to get the authorization token from ACS namespace created in the last section. To avoid writing too much Silverlight specific code, you may consider putting this code in a (WCF or REST) service and let Silverlight app call the service to get ACS token. After you get the ACS token, you may store it in a property (Constants.AcsToken) to be used by the license acquirer immediately after.
  3. The ACS token is then used by a custom license acquirer to acquire a PlayReady license from AMS license delivery service configured in a previous section.


As described in the system diagram above and the author’s blog, this end-to-end prototype contains the following physical components:
  1. Content key and content key ID;
  2. Video asset (unprotected) in Azure Media Services and a streaming origin in Azure Media Services;
  3. PlayReady dynamic protection configured against the asset in Azure Media Services;
  4. PlayReady license delivery service configured in Azure Media Services;
  5. STS configured in Azure ACS 2.0 namespace;
  6. Silverlight video player in an ASP.NET application.
Since the solution is built on Azure Media Services, the only thing you need to deploy is the ASP.NET web application hosting the video player. You have the options to deploy it in
  • Azure web site,
  • Azure IaaS VM, or
  • your on-premise server.
Please make sure that HTTP process activation is installed on the server otherwise WCF service will not work properly. Also please make sure you configure at least 1 RU for the streaming origin to use PlayReady dynamic protection.  

What about Live Streaming?

The good news is that you can use exactly the same design and implementation for protecting live streaming in Azure Media Services, by treating the asset associated with a program as the “VOD asset”. Specifically, it is well known that to do live streaming in Azure Media Services, you need to create a channel, then a program under the channel. To create the program, you need to create an asset which will contain the live archive for the program. All you need to do, is to apply the same setup/processing to the asset as if it was a “VOD asset” before you start the program. The following code shows the precise flow. As shown in the code for Azure Media Services, we use the following method to set up dynamic PlayReady protection for a VOD asset:
public static void SetupDynamicPlayReadyProtection(CloudMediaContext objCloudMediaContext, IAsset objIAsset)
To set up dynamic PlayReady protection for a live streaming, we can create a channel, a program and its asset as usual, but before starting the program, we run the above method against the asset, as shown below.
public static void SetupLiveDynamicPlayReadyProtection(CloudMediaContext objCloudMediaContext, string channelName, string programName, string manifestFileName)
            //get channel
            IChannel objIChannel = objCloudMediaContext.Channels.Where(c => c.Name == channelName).FirstOrDefault();
            Console.WriteLine(string.Format("IChannel.IngestUrl = {0}",  objIChannel.Input.Endpoints.FirstOrDefault().Url.ToString()));
            Console.WriteLine(string.Format("IChannel.PreviewUrl = {0}", objIChannel.Preview.Endpoints.FirstOrDefault().Url.ToString()));

            //create program asset
            IAsset objIAsset = objCloudMediaContext.Assets.Create(string.Format("{0}_Program_Asset_PR", channelName), AssetCreationOptions.None);

            //set up dynamic PlayReady protection for the asset exactly as VOD
            SetupDynamicPlayReadyProtection(objCloudMediaContext, objIAsset);

            //create a program using this asset 
            ProgramCreationOptions options = new ProgramCreationOptions()
                Name                = programName,
                Description         = "Dynamic PlayReady protection for live",
                ArchiveWindowLength = TimeSpan.FromMinutes(120.0),
                ManifestName        = manifestFileName, //manifest file name to be duplicated (without .ism suffix)
                AssetId             = objIAsset.Id
            IProgram objIProgram = objIChannel.Programs.Create(options);

            //publish the asset
            Program.GetStreamingOriginLocator(objIAsset.Id, Program.MediaContentType.SmoothStreaming, true);

            //start the program
            Console.WriteLine("Program {0} has started", programName);
  Of course, this is not the so-called “scalable live TV” PlayReady protection since a single content key is used for protection without the so-called key rotation for leaf and root licenses.


We have presented an end-to-end prototype of Azure Media Services PlayReady solution which includes all of the key components in a DRM solution:
  1. Content key ID and content key generation;
  2. Streaming origin in Azure Media Services;
  3. PlayReady protection via Azure Media Services PlayReady dynamic protection feature;
  4. PlayReady license delivery via Azure Media Services PlayReady license delivery service;
  5. STS (Secure Token Service) via Azure ACS 2.0 to authenticate player client and issue authorization tokens;
  6. Video player application hosted in Azure IaaS VM, which handles ACS authentication, ACS authorization, PlayReady license acquisition and video playback.
  UPDATES: On 1/6/2015: The smooth streaming and MPEG-DASH player has been enhanced for testing PlayReady protection in more general scenarios:
  1. It now works with any Azure ACS namespace instead of just the ACS namespace used for the end-to-end implementation discussed in this blog;
  2. It works with any smooth streaming or MPEG-DASH assets, either open or under dynamic PlayReady protection with or without token restriction;
  3. It works with any PlayReady license server, either key delivery service in Azure Media Services or on premise PlayReady license servers.
  On 1/23/2015: With the release of JWT support in AMS Content Protection, this prototype has been expanded to include token restriction with JWT by using Azure Active Directory (AAD) as both STS and IdP. AMS batch job (for setting up dynamic PlayReady protection or AES encryption): knows AAD tenant, but nothing about player app (any player is fine). AAD tenant: knows player app, but nothing about the AMS batch job. Player app: knows AAD tenant, but nothing about AMS or AMS batch job. In order words, AAD tenant and player app know each other. AMS batch job knows AAD tenant, but does not care what player consumes the contents.   ACKNOWLEDGMENT: Special thanks to Quintin Burns, George Trifonov and Mingfei Yan of Microsoft Azure Media Services Team, who have provided significant help in this effort.