使用 Apple FairPlay 或 Microsoft PlayReady 保护 HLS 内容Protect your HLS content with Apple FairPlay or Microsoft PlayReady

备注

Google Widevine 目前在中国地区不可用。Google Widevine is not avaliable in China regions now.

备注

若要完成本教程,需要一个 Azure 帐户。To complete this tutorial, you need an Azure account. 有关详细信息,请参阅 Azure 1 元试用For details, see Azure 1rmb Trial.

使用 Azure 媒体服务,可使用以下格式动态加密 HTTP Live Streaming (HLS) 内容:Azure Media Services enables you to dynamically encrypt your HTTP Live Streaming (HLS) content by using the following formats:

  • AES-128 信封明文密钥AES-128 envelope clear key

    整个区块使用 AES-128 CBC 模式进行加密。The entire chunk is encrypted by using the AES-128 CBC mode. iOS 和 OS X 播放器本身支持解密流。The decryption of the stream is supported by iOS and OS X player natively. 有关详细信息,请参阅使用 AES-128 动态加密和密钥传递服务For more information, see Using AES-128 dynamic encryption and key delivery service.

  • Apple FairPlayApple FairPlay

    各视频和音频示例都使用 AES-128 CBC 模式进行加密。The individual video and audio samples are encrypted by using the AES-128 CBC mode. FairPlay 流式处理 (FPS) 集成到设备操作系统,iOS 和 Apple TV 本身支持这项功能。FairPlay Streaming (FPS) is integrated into the device operating systems, with native support on iOS and Apple TV. OS X 上的 Safari 使用加密媒体扩展 (EME) 接口支持启用 FPS。Safari on OS X enables FPS by using the Encrypted Media Extensions (EME) interface support.

  • Microsoft PlayReadyMicrosoft PlayReady

下图显示了 HLS + FairPlay 或 PlayReady 动态加密工作流。The following image shows the HLS + FairPlay or PlayReady dynamic encryption workflow.

动态加密工作流的图示

本文演示如何使用媒体服务通过 Apple FairPlay 动态加密 HLS 内容。This article demonstrates how to use Media Services to dynamically encrypt your HLS content with Apple FairPlay. 它还演示了如何使用媒体服务许可证传送服务将 FairPlay 许可证传送到客户端。It also shows how to use the Media Services license delivery service to deliver FairPlay licenses to clients.

备注

如果还想使用 PlayReady 加密 HLS 内容,则需要创建通用的内容密钥并将其与资产相关联。If you also want to encrypt your HLS content with PlayReady, you need to create a common content key and associate it with your asset. 还需要配置内容密钥的授权策略,如使用 PlayReady 动态通用加密中所述。You also need to configure the content key’s authorization policy, as described in Using PlayReady dynamic common encryption.

要求和注意事项Requirements and considerations

在使用媒体服务传送通过 FairPlay 加密的 HLS 和传送 FairPlay 许可证时,需要以下各项:The following are required when using Media Services to deliver HLS encrypted with FairPlay, and to deliver FairPlay licenses:

  • 一个 Azure 帐户。An Azure account. 有关详细信息,请参阅 Azure 试用版For details, see Azure Trial.
  • 一个媒体服务帐户。A Media Services account. 若要创建媒体服务帐户,请参阅使用 Azure 门户创建 Azure 媒体服务帐户To create one, see Create an Azure Media Services account using the Azure portal.
  • 注册 Apple 开发计划Sign up with Apple Development Program.
  • Apple 要求内容所有者获取 部署包Apple requires the content owner to obtain the deployment package. 说明已使用媒体服务实现密钥安全模块 (KSM),以及正在请求最终 FPS 包。State that you already implemented Key Security Module (KSM) with Media Services, and that you are requesting the final FPS package. 最终 FPS 包中有如何生成证书和获取应用程序密钥 (ASK) 的说明。There are instructions in the final FPS package to generate certification and obtain the Application Secret Key (ASK). 可使用 ASK 配置 FairPlay。You use ASK to configure FairPlay.
  • Azure 媒体服务 .NET SDK 3.6.0 版本或更高版本。Azure Media Services .NET SDK version 3.6.0 or later.

必须在媒体服务密钥传送端上设置以下各项:The following things must be set on Media Services key delivery side:

  • 应用证书 (AC) :这是一个包含私钥的 .pfx 文件。App Cert (AC): This is a .pfx file that contains the private key. 创建此文件,并使用密码对其进行加密。You create this file and encrypt it with a password.

    配置密钥传送策略时,必须提供该密码和 Base64 格式的 .pfx 文件。When you configure a key delivery policy, you must provide that password and the .pfx file in Base64 format.

    以下步骤介绍如何为 FairPlay 生成 .pfx 证书文件:The following steps describe how to generate a .pfx certificate file for FairPlay:

    1. https://slproweb.com/products/Win32OpenSSL.html 安装 OpenSSL。Install OpenSSL from https://slproweb.com/products/Win32OpenSSL.html.

      转到 Apple 提供的 FairPlay 证书和其他文件所在的文件夹。Go to the folder where the FairPlay certificate and other files delivered by Apple are.

    2. 从命令行运行以下命令。Run the following command from the command line. 这会将 .cer 文件转换为 .pem 文件。This converts the .cer file to a .pem file.

      "C:\OpenSSL-Win32\bin\openssl.exe" x509 -inform der -in FairPlay.cer -out FairPlay-out.pem"C:\OpenSSL-Win32\bin\openssl.exe" x509 -inform der -in FairPlay.cer -out FairPlay-out.pem

    3. 从命令行运行以下命令。Run the following command from the command line. 这会将 .pem 文件转换为包含私钥的 .pfx 文件。This converts the .pem file to a .pfx file with the private key. 然后, OpenSSL 会要求提供 .pfx 文件的密码。The password for the .pfx file is then asked by OpenSSL.

      "C:\OpenSSL-Win32\bin\openssl.exe" pkcs12 -export -out FairPlay-out.pfx -inkey privatekey.pem -in FairPlay-out.pem -passin file:privatekey-pem-pass.txt"C:\OpenSSL-Win32\bin\openssl.exe" pkcs12 -export -out FairPlay-out.pfx -inkey privatekey.pem -in FairPlay-out.pem -passin file:privatekey-pem-pass.txt

  • 应用证书密码:用于创建 .pfx 文件的密码。App Cert password: The password for creating the .pfx file.

  • 应用证书密码 ID:必须上传密码,其方式与上传其他媒体服务密钥类似。App Cert password ID: You must upload the password, similar to how they upload other Media Services keys. 使用 ContentKeyType.FairPlayPfxPassword 枚举值获取媒体服务 ID。Use the ContentKeyType.FairPlayPfxPassword enum value to get the Media Services ID. 需要在密钥传送策略选项中使用此 ID。This is what they need to use inside the key delivery policy option.

  • iv:这是 16 字节的随机值。iv: This is a random value of 16 bytes. 该值必须与资产传送策略中的 iv 相匹配。It must match the iv in the asset delivery policy. 生成 iv 并将其放入以下两个位置:资产传送策略和密钥传送策略选项。You generate the iv, and put it in both places: the asset delivery policy and the key delivery policy option.

  • ASK:使用 Apple 开发人员门户生成证书时会收到此密钥。ASK: This key is received when you generate the certification by using the Apple Developer portal. 每个开发团队都会收到唯一的 ASK。Each development team receives a unique ASK. 请保存一份 ASK 副本,并将其存储在安全位置。Save a copy of the ASK, and store it in a safe place. 稍后需要将 ASK 作为 FairPlayAsk 配置到媒体服务。You need to configure ASK as FairPlayAsk to Media Services later.

  • ASK ID:将 ASK 上传到媒体服务中时,将获取此 ID。ASK ID: This ID is obtained when you upload ASK into Media Services. 必须使用 ContentKeyType.FairPlayAsk 枚举值上传 ASK。You must upload ASK by using the ContentKeyType.FairPlayAsk enum value. 因此,将返回媒体服务 ID,在设置密钥传送策略选项时应使用此 ID。As the result, the Media Services ID is returned, and this is what should be used when setting the key delivery policy option.

以下事项必须通过 FPS 客户端来设置:The following things must be set by the FPS client side:

  • 应用证书 (AC) :这是一个包含公钥的 .cer/.der 文件,操作系统使用它来加密某些有效负载。App Cert (AC): This is a .cer/.der file that contains the public key, which the operating system uses to encrypt some payload. 媒体服务需要了解它,因为播放器需要它。Media Services needs to know about it because it is required by the player. 密钥传送服务使用相应的私钥对其进行解密。The key delivery service decrypts it using the corresponding private key.

若要播放 FairPlay 加密的流,需要先获取实际 ASK,然后生成实际证书。To play back a FairPlay encrypted stream, get a real ASK first, and then generate a real certificate. 该过程将创建所有三个部分:That process creates all three parts:

  • .der 文件.der file
  • .pfx 文件.pfx file
  • .pfx 的密码password for the .pfx

以下客户端支持使用 AES-128 CBC 加密的 HLS :OS X 上的 Safari、Apple TV、iOS。The following clients support HLS with AES-128 CBC encryption: Safari on OS X, Apple TV, iOS.

配置 FairPlay 动态加密和许可证传送服务Configure FairPlay dynamic encryption and license delivery services

下面是使用 FairPlay 保护资产的常规步骤,这些步骤使用媒体服务许可证传送服务,也使用动态加密。The following are general steps for protecting your assets with FairPlay by using the Media Services license delivery service, and also by using dynamic encryption.

  1. 创建资产并将文件上传到资产。Create an asset, and upload files into the asset.

  2. 将包含文件的资产编码为自适应比特率 MP4 集。Encode the asset that contains the file to the adaptive bitrate MP4 set.

  3. 创建内容密钥并将其与编码资产相关联。Create a content key, and associate it with the encoded asset.

  4. 配置内容密钥授权策略。Configure the content key’s authorization policy. 指定以下项:Specify the following:

    • 传送方法(在本例中为 FairPlay)。The delivery method (in this case, FairPlay).

    • FairPlay 策略选项配置。FairPlay policy options configuration. 有关如何配置 FairPlay 的详细信息,请参阅以下示例中的 ConfigureFairPlayPolicyOptions() 方法。For details on how to configure FairPlay, see the ConfigureFairPlayPolicyOptions() method in the sample below.

      备注

      通常,可能只需配置一次 FairPlay 策略选项,因为仅有一套证书和 ASK。Usually, you would want to configure FairPlay policy options only once, because you will only have one set of a certification and an ASK.

    • 限制(开放或令牌)。Restrictions (open or token).

    • 特定于密钥传送类型的信息,用于定义将密钥传送到客户端的方式。Information specific to the key delivery type that defines how the key is delivered to the client.

  5. 配置资产传送策略。Configure the asset delivery policy. 传送策略配置包括:The delivery policy configuration includes:

    • 传送协议 (HLS)。The delivery protocol (HLS).
    • 动态加密类型(常用的 CBC 加密)。The type of dynamic encryption (common CBC encryption).
    • 许可证获取 URL。The license acquisition URL.
  6. 创建 OnDemand 定位符以获取流式处理 URL。Create an OnDemand locator to get a streaming URL.

使用播放器应用执行的 FairPlay 密钥传送Use FairPlay key delivery by player apps

可以通过使用 iOS SDK 开发播放器应用。You can develop player apps by using the iOS SDK. 若要能够播放 FairPlay 内容,必须实现许可证交换协议。To be able to play FairPlay content, you have to implement the license exchange protocol. 此协议不由 Apple 指定。This protocol is not specified by Apple. 而是取决于每个应用发送密钥传送请求的方式。It is up to each app how to send key delivery requests. 媒体服务 FairPlay 密钥传送服务需要 SPC 为采用以下形式的 www-form-url 编码后消息:The Media Services FairPlay key delivery service expects the SPC to come as a www-form-url encoded post message, in the following form:

spc=<Base64 encoded SPC>

备注

Azure Media Player 支持 FairPlay 播放。Azure Media Player supports FairPlay playback.

流 URLStreaming URLs

如果使用了多个 DRM 加密资产,则应在流式处理 URL 中使用加密标记:(format='m3u8-aapl', encryption='xxx')。If your asset was encrypted with more than one DRM, you should use an encryption tag in the streaming URL: (format='m3u8-aapl', encryption='xxx').

请注意以下事项:The following considerations apply:

  • 仅可以指定零个或一个加密类型。Only zero or one encryption type can be specified.
  • 如果资产仅应用了一种加密,则无需在 URL 中指定加密类型。The encryption type doesn't have to be specified in the URL if only one encryption was applied to the asset.
  • 加密类型不区分大小写。The encryption type is case insensitive.
  • 可以指定以下加密类型:The following encryption types can be specified:
    • cenc:通用加密 (PlayReady)cenc: Common encryption (PlayReady)
    • cbcs-aapl:FairPlaycbcs-aapl: FairPlay
    • cbc:AES 信封加密cbc: AES envelope encryption

创建和配置 Visual Studio 项目Create and configure a Visual Studio project

  1. 设置开发环境,并根据使用 .NET 进行媒体服务开发中所述,在 app.config 文件中填充连接信息。Set up your development environment and populate the app.config file with connection information, as described in Media Services development with .NET.

  2. 将以下元素添加到 app.config 文件中定义的 appSettings:Add the following elements to appSettings defined in your app.config file:

    <add key="Issuer" value="http://testissuer.com"/>
    <add key="Audience" value="urn:test"/>
    

示例Example

以下示例演示使用媒体服务传送使用 FairPlay 加密的内容的能力。The following sample demonstrates the ability to use Media Services to deliver your content encrypted with FairPlay. 适用于 .NET 的 Azure 媒体服务 SDK 版本 3.6.0 中引入了此功能。This functionality was introduced in the Azure Media Services SDK for .NET version 3.6.0.

使用本部分中所示的代码覆盖 Program.cs 文件中的代码。Overwrite the code in your Program.cs file with the code shown in this section.

备注

不同 AMS 策略的策略限制为 1,000,000 个(例如,对于定位器策略或 ContentKeyAuthorizationPolicy)。There is a limit of 1,000,000 policies for different AMS policies (for example, for Locator policy or ContentKeyAuthorizationPolicy). 如果始终使用相同的日期/访问权限,则应使用相同的策略 ID,例如,用于要长期就地保留的定位符的策略(非上传策略)。You should use the same policy ID if you are always using the same days / access permissions, for example, policies for locators that are intended to remain in place for a long time (non-upload policies). 有关详细信息,请参阅本文For more information, see this article.

请务必将变量更新为指向输入文件所在的文件夹。Make sure to update variables to point to folders where your input files are located.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.WindowsAzure.MediaServices.Client;
using Microsoft.WindowsAzure.MediaServices.Client.ContentKeyAuthorization;
using Microsoft.WindowsAzure.MediaServices.Client.DynamicEncryption;
using Microsoft.WindowsAzure.MediaServices.Client.FairPlay;
using Newtonsoft.Json;
using System.Security.Cryptography.X509Certificates;

namespace DynamicEncryptionWithFairPlay
{
    class Program
    {
        // Read values from the App.config file.
        private static readonly string _AADTenantDomain =
            ConfigurationManager.AppSettings["AMSAADTenantDomain"];
        private static readonly string _RESTAPIEndpoint =
            ConfigurationManager.AppSettings["AMSRESTAPIEndpoint"];
        private static readonly string _AMSClientId =
            ConfigurationManager.AppSettings["AMSClientId"];
        private static readonly string _AMSClientSecret =
            ConfigurationManager.AppSettings["AMSClientSecret"];

        private static readonly Uri _sampleIssuer =
            new Uri(ConfigurationManager.AppSettings["Issuer"]);
        private static readonly Uri _sampleAudience =
            new Uri(ConfigurationManager.AppSettings["Audience"]);

        // Field for service context.
        private static CloudMediaContext _context = null;

        private static readonly string _mediaFiles =
            Path.GetFullPath(@"../..\Media");

        private static readonly string _singleMP4File =
            Path.Combine(_mediaFiles, @"BigBuckBunny.mp4");

        static void Main(string[] args)
        {
            AzureAdTokenCredentials tokenCredentials =
                new AzureAdTokenCredentials(_AADTenantDomain,
                    new AzureAdClientSymmetricKey(_AMSClientId, _AMSClientSecret),
                    AzureEnvironments.AzureChinaCloudEnvironment);

            var tokenProvider = new AzureAdTokenProvider(tokenCredentials);

            _context = new CloudMediaContext(new Uri(_RESTAPIEndpoint), tokenProvider);

            bool tokenRestriction = false;
            string tokenTemplateString = null;

            IAsset asset = UploadFileAndCreateAsset(_singleMP4File);
            Console.WriteLine("Uploaded asset: {0}", asset.Id);

            IAsset encodedAsset = EncodeToAdaptiveBitrateMP4Set(asset);
            Console.WriteLine("Encoded asset: {0}", encodedAsset.Id);

            IContentKey key = CreateCommonCBCTypeContentKey(encodedAsset);
            Console.WriteLine("Created key {0} for the asset {1} ", key.Id, encodedAsset.Id);
            Console.WriteLine("FairPlay License Key delivery URL: {0}", key.GetKeyDeliveryUrl(ContentKeyDeliveryType.FairPlay));
            Console.WriteLine();

            if (tokenRestriction)
                tokenTemplateString = AddTokenRestrictedAuthorizationPolicy(key);
            else
                AddOpenAuthorizationPolicy(key);

            Console.WriteLine("Added authorization policy: {0}", key.AuthorizationPolicyId);
            Console.WriteLine();

            CreateAssetDeliveryPolicy(encodedAsset, key);
            Console.WriteLine("Created asset delivery policy. \n");
            Console.WriteLine();

            if (tokenRestriction && !String.IsNullOrEmpty(tokenTemplateString))
            {
                // Deserializes a string containing an Xml representation of a TokenRestrictionTemplate
                // back into a TokenRestrictionTemplate class instance.
                TokenRestrictionTemplate tokenTemplate =
                    TokenRestrictionTemplateSerializer.Deserialize(tokenTemplateString);

                // Generate a test token based on the data in the given TokenRestrictionTemplate.
                // Note, you need to pass the key id Guid because we specified
                // TokenClaim.ContentKeyIdentifierClaim in during the creation of TokenRestrictionTemplate.
                Guid rawkey = EncryptionUtils.GetKeyIdAsGuid(key.Id);
                string testToken = TokenRestrictionTemplateSerializer.GenerateTestToken(tokenTemplate, null, rawkey,
                                            DateTime.UtcNow.AddDays(365));
                Console.WriteLine("The authorization token is:\nBearer {0}", testToken);
                Console.WriteLine();
            }

            string url = GetStreamingOriginLocator(encodedAsset);
            Console.WriteLine("Encrypted HLS URL: {0}/manifest(format=m3u8-aapl)", url);

            Console.ReadLine();
        }

        static public IAsset UploadFileAndCreateAsset(string singleFilePath)
        {
            if (!File.Exists(singleFilePath))
            {
                Console.WriteLine("File does not exist.");
                return null;
            }

            var assetName = Path.GetFileNameWithoutExtension(singleFilePath);
            IAsset inputAsset = _context.Assets.Create(assetName, AssetCreationOptions.None);

            var assetFile = inputAsset.AssetFiles.Create(Path.GetFileName(singleFilePath));

            Console.WriteLine("Created assetFile {0}", assetFile.Name);

            Console.WriteLine("Upload {0}", assetFile.Name);

            assetFile.Upload(singleFilePath);
            Console.WriteLine("Done uploading {0}", assetFile.Name);

            return inputAsset;
        }

        static public IAsset EncodeToAdaptiveBitrateMP4Set(IAsset inputAsset)
        {
            var encodingPreset = "Adaptive Streaming";

            IJob job = _context.Jobs.Create(String.Format("Encoding {0}", inputAsset.Name));

            var mediaProcessors =
            _context.MediaProcessors.Where(p => p.Name.Contains("Media Encoder Standard")).ToList();

            var latestMediaProcessor =
            mediaProcessors.OrderBy(mp => new Version(mp.Version)).LastOrDefault();

            ITask encodeTask = job.Tasks.AddNew("Encoding", latestMediaProcessor, encodingPreset, TaskOptions.None);
            encodeTask.InputAssets.Add(inputAsset);
            encodeTask.OutputAssets.AddNew(String.Format("{0} as {1}", inputAsset.Name, encodingPreset), AssetCreationOptions.StorageEncrypted);

            job.StateChanged += new EventHandler<JobStateChangedEventArgs>(JobStateChanged);
            job.Submit();
            job.GetExecutionProgressTask(CancellationToken.None).Wait();

            return job.OutputMediaAssets[0];
        }

        static public IContentKey CreateCommonCBCTypeContentKey(IAsset asset)
        {
            // Create HLS SAMPLE AES encryption content key
            Guid keyId = Guid.NewGuid();
            byte[] contentKey = GetRandomBuffer(16);

            IContentKey key = _context.ContentKeys.Create(
                        keyId,
                        contentKey,
                        "ContentKey",
                        ContentKeyType.CommonEncryptionCbcs);

            // Associate the key with the asset.
            asset.ContentKeys.Add(key);

            return key;
        }


        static public void AddOpenAuthorizationPolicy(IContentKey contentKey)
        {
            // Create ContentKeyAuthorizationPolicy with Open restrictions
            // and create authorization policy          

            List<ContentKeyAuthorizationPolicyRestriction> restrictions = new List<ContentKeyAuthorizationPolicyRestriction>
                    {
                    new ContentKeyAuthorizationPolicyRestriction
                    {
                        Name = "Open",
                        KeyRestrictionType = (int)ContentKeyRestrictionType.Open,
                        Requirements = null
                    }
                    };


            // Configure FairPlay policy option.
            string FairPlayConfiguration = ConfigureFairPlayPolicyOptions();

            IContentKeyAuthorizationPolicyOption FairPlayPolicy =
            _context.ContentKeyAuthorizationPolicyOptions.Create("",
            ContentKeyDeliveryType.FairPlay,
            restrictions,
            FairPlayConfiguration);


            IContentKeyAuthorizationPolicy contentKeyAuthorizationPolicy = _context.
                ContentKeyAuthorizationPolicies.
                CreateAsync("Deliver Common CBC Content Key with no restrictions").
                Result;

            contentKeyAuthorizationPolicy.Options.Add(FairPlayPolicy);

            // Associate the content key authorization policy with the content key.
            contentKey.AuthorizationPolicyId = contentKeyAuthorizationPolicy.Id;
            contentKey = contentKey.UpdateAsync().Result;
        }

        public static string AddTokenRestrictedAuthorizationPolicy(IContentKey contentKey)
        {
            string tokenTemplateString = GenerateTokenRequirements();

            List<ContentKeyAuthorizationPolicyRestriction> restrictions = new List<ContentKeyAuthorizationPolicyRestriction>
                    {
                    new ContentKeyAuthorizationPolicyRestriction
                    {
                        Name = "Token Authorization Policy",
                        KeyRestrictionType = (int)ContentKeyRestrictionType.TokenRestricted,
                        Requirements = tokenTemplateString,
                    }
                    };

            // Configure FairPlay policy option.
            string FairPlayConfiguration = ConfigureFairPlayPolicyOptions();


            IContentKeyAuthorizationPolicyOption FairPlayPolicy =
            _context.ContentKeyAuthorizationPolicyOptions.Create("Token option",
                   ContentKeyDeliveryType.FairPlay,
                   restrictions,
                   FairPlayConfiguration);

            IContentKeyAuthorizationPolicy contentKeyAuthorizationPolicy = _context.
                ContentKeyAuthorizationPolicies.
                CreateAsync("Deliver Common CBC Content Key with token restrictions").
                Result;

            contentKeyAuthorizationPolicy.Options.Add(FairPlayPolicy);

            // Associate the content key authorization policy with the content key
            contentKey.AuthorizationPolicyId = contentKeyAuthorizationPolicy.Id;
            contentKey = contentKey.UpdateAsync().Result;

            return tokenTemplateString;
        }

        private static string ConfigureFairPlayPolicyOptions()
        {
            // For testing you can provide all zeroes for ASK bytes together with the cert from Apple FPS SDK.
            // However, for production you must use a real ASK from Apple bound to a real prod certificate.
            byte[] askBytes = Guid.NewGuid().ToByteArray();
            var askId = Guid.NewGuid();
            // Key delivery retrieves askKey by askId and uses this key to generate the response.
            IContentKey askKey = _context.ContentKeys.Create(
                        askId,
                        askBytes,
                        "askKey",
                        ContentKeyType.FairPlayASk);

            //Customer password for creating the .pfx file.
            string pfxPassword = "<customer password for creating the .pfx file>";
            // Key delivery retrieves pfxPasswordKey by pfxPasswordId and uses this key to generate the response.
            var pfxPasswordId = Guid.NewGuid();
            byte[] pfxPasswordBytes = System.Text.Encoding.UTF8.GetBytes(pfxPassword);
            IContentKey pfxPasswordKey = _context.ContentKeys.Create(
                        pfxPasswordId,
                        pfxPasswordBytes,
                        "pfxPasswordKey",
                        ContentKeyType.FairPlayPfxPassword);

            // iv - 16 bytes random value, must match the iv in the asset delivery policy.
            byte[] iv = Guid.NewGuid().ToByteArray();

            //Specify the .pfx file created by the customer.
            var appCert = new X509Certificate2("path to the .pfx file created by the customer", pfxPassword, X509KeyStorageFlags.Exportable);

            string FairPlayConfiguration =
            Microsoft.WindowsAzure.MediaServices.Client.FairPlay.FairPlayConfiguration.CreateSerializedFairPlayOptionConfiguration(
                appCert,
                pfxPassword,
                pfxPasswordId,
                askId,
                iv);

            return FairPlayConfiguration;
        }

        static private string GenerateTokenRequirements()
        {
            TokenRestrictionTemplate template = new TokenRestrictionTemplate(TokenType.SWT);

            template.PrimaryVerificationKey = new SymmetricVerificationKey();
            template.AlternateVerificationKeys.Add(new SymmetricVerificationKey());
            template.Audience = _sampleAudience.ToString();
            template.Issuer = _sampleIssuer.ToString();
            template.RequiredClaims.Add(TokenClaim.ContentKeyIdentifierClaim);

            return TokenRestrictionTemplateSerializer.Serialize(template);
        }

        static public void CreateAssetDeliveryPolicy(IAsset asset, IContentKey key)
        {
            var kdPolicy = _context.ContentKeyAuthorizationPolicies.Where(p => p.Id == key.AuthorizationPolicyId).Single();

            var kdOption = kdPolicy.Options.Single(o => o.KeyDeliveryType == ContentKeyDeliveryType.FairPlay);

            FairPlayConfiguration configFP = JsonConvert.DeserializeObject<FairPlayConfiguration>(kdOption.KeyDeliveryConfiguration);

            // Get the FairPlay license service URL.
            Uri acquisitionUrl = key.GetKeyDeliveryUrl(ContentKeyDeliveryType.FairPlay);

            // The reason the below code replaces "https://" with "skd://" is because
            // in the IOS player sample code which you obtained in Apple developer account,
            // the player only recognizes a Key URL that starts with skd://.
            // However, if you are using a customized player,
            // you can choose whatever protocol you want.
            // For example, "https".

            Dictionary<AssetDeliveryPolicyConfigurationKey, string> assetDeliveryPolicyConfiguration =
            new Dictionary<AssetDeliveryPolicyConfigurationKey, string>
            {
                    {AssetDeliveryPolicyConfigurationKey.FairPlayLicenseAcquisitionUrl, acquisitionUrl.ToString().Replace("https://", "skd://")},
                    {AssetDeliveryPolicyConfigurationKey.CommonEncryptionIVForCbcs, configFP.ContentEncryptionIV}
            };

            var assetDeliveryPolicy = _context.AssetDeliveryPolicies.Create(
                "AssetDeliveryPolicy",
            AssetDeliveryPolicyType.DynamicCommonEncryptionCbcs,
            AssetDeliveryProtocol.HLS,
            assetDeliveryPolicyConfiguration);

            // Add AssetDelivery Policy to the asset
            asset.DeliveryPolicies.Add(assetDeliveryPolicy);

        }


        /// <summary>
        /// Gets the streaming origin locator.
        /// </summary>
        /// <param name="assets"></param>
        /// <returns></returns>
        static public string GetStreamingOriginLocator(IAsset asset)
        {

            // Get a reference to the streaming manifest file from the  
            // collection of files in the asset.

            var assetFile = asset.AssetFiles.LoList().Where(f => f.Name.ToLower().
                         EndsWith(".ism")).
                         FirstOrDefault();

            // Create a 30-day readonly access policy.
            IAccessPolicy policy = _context.AccessPolicies.Create("Streaming policy",
            TimeSpan.FromDays(30),
            AccessPermissions.Read);

            // Create a locator to the streaming content on an origin.
            ILocator originLocator = _context.Locators.CreateLocator(LocatorType.OnDemandOrigin, asset,
            policy,
            DateTime.UtcNow.AddMinutes(-5));

            // Create a URL to the manifest file.
            return originLocator.Path + assetFile.Name;
        }

        static private void JobStateChanged(object sender, JobStateChangedEventArgs e)
        {
            Console.WriteLine(string.Format("{0}\n  State: {1}\n  Time: {2}\n\n",
            ((IJob)sender).Name,
            e.CurrentState,
            DateTime.UtcNow.ToString(@"yyyy_M_d__hh_mm_ss")));
        }

        static private byte[] GetRandomBuffer(int length)
        {
            var returnValue = new byte[length];

            using (var rng =
            new System.Security.Cryptography.RNGCryptoServiceProvider())
            {
                rng.GetBytes(returnValue);
            }

            return returnValue;
        }
    }
}

后续步骤:媒体服务学习路径Next steps: Media Services learning paths

媒体服务 v3(最新版本)Media Services v3 (latest)

查看最新版本的 Azure 媒体服务!Check out the latest version of Azure Media Services!

媒体服务 v2(旧版)Media Services v2 (legacy)