教程:使用 AES-128 来加密视频以及使用密钥传送服务Tutorial: Encrypt video with AES-128 and use the key delivery service

备注

Google Widevine 内容保护服务目前在 Azure 中国区域不可用。Google Widevine content protection services are currently unavailable in the Azure China regions.

备注

尽管本教程使用了 .NET SDK 示例,但 REST APICLI 或其他受支持的 SDK 的常规步骤是相同的。Even though the tutorial uses the .NET SDK examples, the general steps are the same for REST API, CLI, or other supported SDKs.

借助媒体服务,可以传送使用 AES 通过 128 位加密密钥加密的 HTTP Live Streaming (HLS)、MPEG-DASH 和平滑流。You can use Media Services to deliver HTTP Live Streaming (HLS), MPEG-DASH, and Smooth Streaming encrypted with the AES by using 128-bit encryption keys. 媒体服务还提供密钥传送服务,将加密密钥传送给已授权的用户。Media Services also provides the key delivery service that delivers encryption keys to authorized users. 如果希望媒体服务动态加密视频,需要将加密密钥与流定位器相关联,并配置内容密钥策略。If you want for Media Services to dynamically encrypt your video, you associate the encryption key with a Streaming Locator and also configure the content key policy. 当播放器请求流时,媒体服务将使用指定的密钥通过 AES-128 动态加密内容。When a stream is requested by a player, Media Services uses the specified key to dynamically encrypt your content with AES-128. 为了解密流,播放器将从密钥传送服务请求密钥。To decrypt the stream, the player requests the key from the key delivery service. 为了确定是否已授权用户获取密钥,服务将评估你为密钥指定的内容密钥策略。To determine whether the user is authorized to get the key, the service evaluates the content key policy that you specified for the key.

可以使用多个加密类型(AES-128、PlayReady、FairPlay)来加密每个资产。You can encrypt each asset with multiple encryption types (AES-128, PlayReady, FairPlay). 请参阅流式处理协议和加密类型,以了解有效的组合方式。See Streaming protocols and encryption types, to see what makes sense to combine. 另请参阅如何使用 DRM 进行保护Also, see How to protect with DRM.

本文中示例的输出包括 Azure Media Player 的 URL、清单 URL,以及播放内容所需的 AES 令牌。The output from the sample this article includes a URL to the Azure Media Player, manifest URL, and the AES token needed to play back the content. 该示例将 JSON Web 令牌 (JWT) 令牌的过期时间设置为 1 小时。The sample sets the expiration of the JSON Web Token (JWT) token to 1 hour. 可以打开浏览器并粘贴生成的 URL 来启动 Azure Media Player 演示页,其中已经填充了该 URL 和令牌(采用以下格式:https://ampdemo.azureedge.net/?url= {dash Manifest URL} &aes=true&aestoken=Bearer%3D{ JWT Token here})。You can open a browser and paste the resulting URL to launch the Azure Media Player demo page with the URL and token filled out for you already in the following format: https://ampdemo.azureedge.net/?url= {dash Manifest URL} &aes=true&aestoken=Bearer%3D{ JWT Token here}.

本教程演示如何:This tutorial shows you how to:

  • 下载本文中介绍的 EncryptWithAES 示例。Download the EncryptWithAES sample described in the article.
  • 开始结合使用媒体服务 API 与 .NET SDK。Start using Media Services APIs with .NET SDK.
  • 创建输出资产。Create an output asset.
  • 创建编码转换。Create an encoding Transform.
  • 提交作业。Submit a Job.
  • 等待作业完成。Wait for the Job to complete.
  • 创建内容密钥策略Create a Content Key Policy
  • 配置使用 JWT 令牌限制所需的策略。Configure the policy to use JWT token restriction.
  • 创建流式处理定位符。Create a Streaming Locator.
  • 配置流定位器,以便使用 AES (ClearKey) 加密视频。Configure the Streaming Locator to encrypt the video with AES (ClearKey).
  • 获取测试令牌。Get a test token.
  • 生成流 URL。Build a streaming URL.
  • 清理资源。Clean up resources.

如果没有 Azure 订阅,可在开始前创建一个试用帐户If you don't have an Azure subscription, create a trial account before you begin.

先决条件Prerequisites

以下是完成本教程所需具备的条件。The following are required to complete the tutorial.

下载代码Download code

使用以下命令,将包含本文中所述完整 .NET 示例的 GitHub 存储库克隆到计算机:Clone a GitHub repository that contains the full .NET sample discussed in this article to your machine using the following command:

git clone https://github.com/Azure-Samples/media-services-v3-dotnet-tutorials.git

“使用 AES-128 加密”示例位于 EncryptWithAES 文件夹中。The "Encrypt with AES-128" sample is located in the EncryptWithAES folder.

备注

每次运行应用时,该示例就会创建唯一的资源。The sample creates unique resources every time you run the app. 通常,我们会重复使用现有的资源,例如转换和策略(如果现有资源具有所需的配置)。Typically, you'll reuse existing resources like transforms and policies (if existing resource have required configurations).

开始结合使用媒体服务 API 与 .NET SDKStart using Media Services APIs with .NET SDK

若要开始将媒体服务 API 与 .NET 结合使用,请创建 AzureMediaServicesClient 对象 。To start using Media Services APIs with .NET, create an AzureMediaServicesClient object. 若要创建对象,需要提供客户端所需凭据以使用 Azure AD 连接到 Azure。To create the object, you need to supply credentials needed for the client to connect to Azure using Azure AD. 在本文开头克隆的代码中,GetCredentialsAsync 函数根据本地配置文件中提供的凭据创建 ServiceClientCredentials 对象。In the code you cloned at the beginning of the article, the GetCredentialsAsync function creates the ServiceClientCredentials object based on the credentials supplied in the local configuration file.

private static async Task<IAzureMediaServicesClient> CreateMediaServicesClientAsync(ConfigWrapper config)
{
    var credentials = await GetCredentialsAsync(config);

    return new AzureMediaServicesClient(config.ArmEndpoint, credentials)
    {
        SubscriptionId = config.SubscriptionId,
    };
}

创建输出资产Create an output asset

输出资产会存储作业编码的结果。The output Asset stores the result of your encoding job.

private static async Task<Asset> CreateOutputAssetAsync(IAzureMediaServicesClient client, string resourceGroupName, string accountName, string assetName)
{
    // Check if an Asset already exists
    Asset outputAsset = await client.Assets.GetAsync(resourceGroupName, accountName, assetName);
    Asset asset = new Asset();
    string outputAssetName = assetName;

    if (outputAsset != null)
    {
        // Name collision! In order to get the sample to work, let's just go ahead and create a unique asset name
        // Note that the returned Asset can have a different name than the one specified as an input parameter.
        // You may want to update this part to throw an Exception instead, and handle name collisions differently.
        string uniqueness = $"-{Guid.NewGuid().ToString("N")}";
        outputAssetName += uniqueness;
        
        Console.WriteLine("Warning – found an existing Asset with name = " + assetName);
        Console.WriteLine("Creating an Asset with this name instead: " + outputAssetName);                
    }

    return await client.Assets.CreateOrUpdateAsync(resourceGroupName, accountName, outputAssetName, asset);
}

获取或创建编码转换Get or create an encoding Transform

创建新实例时,需要指定希望生成的输出内容转换When creating a new Transform instance, you need to specify what you want it to produce as an output. 所需参数是 TransformOutput 对象,如以下代码所示。The required parameter is a TransformOutput object, as shown in the code below. 每个 TransformOutput 包含一个预设 。Each TransformOutput contains a Preset. 预设介绍了视频和/或音频处理操作的分步说明,这些操作将用于生成所需的 TransformOutput 。Preset describes the step-by-step instructions of video and/or audio processing operations that are to be used to generate the desired TransformOutput. 本文中的示例使用名为 AdaptiveStreaming 的内置预设 。The sample described in this article uses a built-in Preset called AdaptiveStreaming. 此预设将输入的视频编码为基于输入的分辨率和比特率自动生成的比特率阶梯(比特率 - 分辨率对),然后通过与每个比特率 - 分辨率对相对应的 H.264 视频和 AAC 音频生成 ISO MP4 文件。The Preset encodes the input video into an autogenerated bitrate ladder (bitrate-resolution pairs) based on the input resolution and bitrate, and then produces ISO MP4 files with H.264 video and AAC audio corresponding to each bitrate-resolution pair.

在创建新的转换之前,请先检查是否已存在使用 Get 方法的转换,如以下代码中所示。Before creating a new Transform, first check if one already exists using the Get method, as shown in the code that follows. 在 Media Services v3获取实体上的方法返回null如果实体不存在 (不区分大小写的名称检查)。In Media Services v3, Get methods on entities return null if the entity doesn’t exist (a case-insensitive check on the name).

private static async Task<Transform> GetOrCreateTransformAsync(
    IAzureMediaServicesClient client,
    string resourceGroupName,
    string accountName,
    string transformName)
{
    // Does a Transform already exist with the desired name? Assume that an existing Transform with the desired name
    // also uses the same recipe or Preset for processing content.
    Transform transform = await client.Transforms.GetAsync(resourceGroupName, accountName, transformName);

    if (transform == null)
    {
        // You need to specify what you want it to produce as an output
        TransformOutput[] output = new TransformOutput[]
        {
            new TransformOutput
            {
                // The preset for the Transform is set to one of Media Services built-in sample presets.
                // You can  customize the encoding settings by changing this to use "StandardEncoderPreset" class.
                Preset = new BuiltInStandardEncoderPreset()
                {
                    // This sample uses the built-in encoding preset for Adaptive Bitrate Streaming.
                    PresetName = EncoderNamedPreset.AdaptiveStreaming
                }
            }
        };

        // Create the Transform with the output defined above
        transform = await client.Transforms.CreateOrUpdateAsync(resourceGroupName, accountName, transformName, output);
    }

    return transform;
}

提交作业Submit Job

如上所述,转换对象为脚本,作业则是对媒体服务的实际请求,请求将转换应用到给定输入视频或音频内容 。As mentioned above, the Transform object is the recipe and a Job is the actual request to Media Services to apply that Transform to a given input video or audio content. Job 指定输入视频位置和输出位置等信息 。The Job specifies information like the location of the input video and the location for the output.

本教程基于直接从 HTTPs 源 URL 引入的文件创建作业的输入。In this tutorial, we create the job's input based on a file that's ingested directly from an HTTPs source URL.

private static async Task<Job> SubmitJobAsync(IAzureMediaServicesClient client,
    string resourceGroup,
    string accountName,
    string transformName,
    string outputAssetName,
    string jobName)
{
    // This example shows how to encode from any HTTPs source URL - a new feature of the v3 API.  
    // Change the URL to any accessible HTTPs URL or SAS URL from Azure.
    JobInputHttp jobInput =
        new JobInputHttp(files: new[] { "https://nimbuscdn-nimbuspm.streaming.mediaservices.windows.net/2b533311-b215-4409-80af-529c3e853622/Ignite-short.mp4" });

    JobOutput[] jobOutputs =
    {
        new JobOutputAsset(outputAssetName),
    };

    // In this example, we are assuming that the job name is unique.
    //
    // If you already have a job with the desired name, use the Jobs.Get method
    // to get the existing job. In Media Services v3, the Get method on entities returns null 
    // if the entity doesn't exist (a case-insensitive check on the name).
    Job job = await client.Jobs.CreateAsync(
        resourceGroup,
        accountName,
        transformName,
        jobName,
        new Job
        {
            Input = jobInput,
            Outputs = jobOutputs,
        });

    return job;
}

等待作业完成Wait for the Job to complete

该作业需要一些时间才能完成操作。The job takes some time to complete. 在该过程中,你应能够接收通知。When it does, you want to be notified. 以下代码示例显示如何轮询服务以获取作业状态。The code sample below shows how to poll the service for the status of the Job.

作业通常会经历以下状态:已计划已排队正在处理已完成(最终状态)。The Job usually goes through the following states: Scheduled, Queued, Processing, Finished (the final state). 如果作业出错,则显示“错误”状态 。If the job has come across an error, you get the Error state. 如果作业正处于取消过程中,则显示“正在取消”,完成时则显示“已取消” 。If the job is in the process of being canceled, you get Canceling and Canceled when it's done.

private static async Task<Job> WaitForJobToFinishAsync(IAzureMediaServicesClient client,
    string resourceGroupName,
    string accountName,
    string transformName,
    string jobName)
{
    const int SleepIntervalMs = 60 * 1000;

    Job job = null;

    do
    {
        job = await client.Jobs.GetAsync(resourceGroupName, accountName, transformName, jobName);

        Console.WriteLine($"Job is '{job.State}'.");
        for (int i = 0; i < job.Outputs.Count; i++)
        {
            JobOutput output = job.Outputs[i];
            Console.Write($"\tJobOutput[{i}] is '{output.State}'.");
            if (output.State == JobState.Processing)
            {
                Console.Write($"  Progress: '{output.Progress}'.");
            }

            Console.WriteLine();
        }

        if (job.State != JobState.Finished && job.State != JobState.Error && job.State != JobState.Canceled)
        {
            await Task.Delay(SleepIntervalMs);
        }
    }
    while (job.State != JobState.Finished && job.State != JobState.Error && job.State != JobState.Canceled);

    return job;
}

创建内容密钥策略Create a Content Key Policy

内容密钥提供对资产的安全访问。A content key provides secure access to your Assets. 需要创建一个内容密钥策略,用于配置如何将内容密钥传送到终端客户端。You need to create a Content Key Policy that configures how the content key is delivered to end clients. 内容密钥与流定位器相关联。The content key is associated with the Streaming Locator. 媒体服务还提供密钥传送服务,将加密密钥传送给已授权的用户。Media Services also provides the key delivery service that delivers encryption keys to authorized users.

当播放器请求流时,媒体服务将使用指定的密钥通过 AES 加密来动态加密内容(本例中使用 AES 加密)。为了解密流,播放器将从密钥传送服务请求密钥。When a stream is requested by a player, Media Services uses the specified key to dynamically encrypt your content (in this case, by using AES encryption.) To decrypt the stream, the player requests the key from the key delivery service. 为了确定是否已授权用户获取密钥,服务将评估你为密钥指定的内容密钥策略。To determine whether the user is authorized to get the key, the service evaluates the content key policy that you specified for the key.

private static async Task<ContentKeyPolicy> GetOrCreateContentKeyPolicyAsync(
    IAzureMediaServicesClient client,
    string resourceGroupName,
    string accountName,
    string contentKeyPolicyName)
{
    ContentKeyPolicy policy = await client.ContentKeyPolicies.GetAsync(resourceGroupName, accountName, contentKeyPolicyName);

    if (policy == null)
    {

        ContentKeyPolicySymmetricTokenKey primaryKey = new ContentKeyPolicySymmetricTokenKey(TokenSigningKey);
        List<ContentKeyPolicyRestrictionTokenKey> alternateKeys = null;
        List<ContentKeyPolicyTokenClaim> requiredClaims = new List<ContentKeyPolicyTokenClaim>()
        {
            ContentKeyPolicyTokenClaim.ContentKeyIdentifierClaim
        };

        List<ContentKeyPolicyOption> options = new List<ContentKeyPolicyOption>()
        {
            new ContentKeyPolicyOption(
                new ContentKeyPolicyClearKeyConfiguration(),
                new ContentKeyPolicyTokenRestriction(Issuer, Audience, primaryKey,
                    ContentKeyPolicyRestrictionTokenType.Jwt, alternateKeys, requiredClaims))
        };

        // Since we are randomly generating the signing key each time, make sure to create or update the policy each time.
        // Normally you would use a long lived key so you would just check for the policies existence with Get instead of
        // ensuring to create or update it each time.
        policy = await client.ContentKeyPolicies.CreateOrUpdateAsync(resourceGroupName, accountName, contentKeyPolicyName, options);

    }

    return policy;
}

创建流定位符Create a Streaming Locator

完成编码并设置内容密钥策略后,下一步是使输出资产中的视频可供客户端播放。After the encoding is complete, and the content key policy is set, the next step is to make the video in the output Asset available to clients for playback. 可以通过两个步骤来提供视频:You make the video available in two steps:

  1. 创建流式处理定位符Create a Streaming Locator.
  2. 生成客户端可以使用的流式处理 URL。Build the streaming URLs that clients can use.

创建流定位器的过程称为发布。The process of creating the Streaming Locator is called publishing. 默认情况下,除非配置可选的开始和结束时间,否则调用 API 后,流式处理定位符 立即生效,By default, the Streaming Locator is valid immediately after you make the API calls. 并持续到被删除为止。It lasts until it's deleted, unless you configure the optional start and end times.

创建流式处理定位符时,需要指定所需的 StreamingPolicyNameWhen creating a Streaming Locator, you'll need to specify the desired StreamingPolicyName. 本教程使用某个 PredefinedStreamingPolicies 来告知 Azure 媒体服务如何发布流式处理的内容。In this tutorial, we're using one of the PredefinedStreamingPolicies, which tells Azure Media Services how to publish the content for streaming. 此示例中应用了 AES 信封加密(此加密也称为 ClearKey 加密,因为密钥是通过 HTTPS 而不是 DRM 许可证传送到播放客户端的)。In this example, the AES Envelope encryption is applied (this encryption is also known as ClearKey encryption because the key is delivered to the playback client via HTTPS and not a DRM license).

重要

使用自定义的 StreamingPolicy 时,应为媒体服务帐户设计有限的一组此类策略,并在需要同样的加密选项和协议时重新将这些策略用于流式处理定位符。When using a custom StreamingPolicy, you should design a limited set of such policies for your Media Service account, and re-use them for your Streaming Locators whenever the same encryption options and protocols are needed. 媒体服务帐户具有对应于 StreamingPolicy 条目数的配额。Your Media Service account has a quota for the number of StreamingPolicy entries. 不应为每个流式处理定位符创建新的 StreamingPolicy。You shouldn't be creating a new StreamingPolicy for each Streaming Locator.

private static async Task<StreamingLocator> CreateStreamingLocatorAsync(
    IAzureMediaServicesClient client,
    string resourceGroup,
    string accountName,
    string assetName,
    string locatorName,
    string contentPolicyName)
{
    StreamingLocator locator = await client.StreamingLocators.CreateAsync(
        resourceGroup,
        accountName,
        locatorName,
        new StreamingLocator
        {
            AssetName = assetName,
            StreamingPolicyName = PredefinedStreamingPolicy.ClearKey,
            DefaultContentKeyPolicyName = contentPolicyName
        });

    return locator;
}

获取测试令牌Get a test token

本教程在内容密钥策略中指定使用令牌限制。In this tutorial, we specify for the content key policy to have a token restriction. 令牌限制策略必须附带由安全令牌服务 (STS) 颁发的令牌。The token-restricted policy must be accompanied by a token issued by a security token service (STS). 媒体服务支持采用 JWT 格式的令牌,我们在示例中配置了此格式。Media Services supports tokens in the JWT format and that's what we configure in the sample.

内容密钥策略中使用了 ContentKeyIdentifierClaim,这意味着,提供给密钥传送服务的令牌必须包含内容密钥的标识符。The ContentKeyIdentifierClaim is used in the Content Key Policy, which means that the token presented to the Key Delivery service must have the identifier of the content key in it. 本示例未指定内容密钥,在创建流定位器时,系统创建了一个随机内容密钥。In the sample, we didn't specify a content key when creating the Streaming Locator, the system created a random one for us. 若要生成测试令牌,必须获取要放入 ContentKeyIdentifierClaim 声明中的 ContentKeyId。To generate the test token, we must get the ContentKeyId to put in the ContentKeyIdentifierClaim claim.

private static string GetTokenAsync(string issuer, string audience, string keyIdentifier, byte[] tokenVerificationKey)
{
    var tokenSigningKey = new SymmetricSecurityKey(tokenVerificationKey);

    SigningCredentials cred = new SigningCredentials(
        tokenSigningKey,
        // Use the  HmacSha256 and not the HmacSha256Signature option, or the token will not work!
        SecurityAlgorithms.HmacSha256,
        SecurityAlgorithms.Sha256Digest);

    Claim[] claims = new Claim[]
    {
        new Claim(ContentKeyPolicyTokenClaim.ContentKeyIdentifierClaim.ClaimType, keyIdentifier)
    };

    JwtSecurityToken token = new JwtSecurityToken(
        issuer: issuer,
        audience: audience,
        claims: claims,
        notBefore: DateTime.Now.AddMinutes(-5),
        expires: DateTime.Now.AddMinutes(60),
        signingCredentials: cred);

    JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();

    return handler.WriteToken(token);
}

生成 DASH 流 URLBuild a DASH streaming URL

创建流定位器后,即可获取流式处理 URL。Now that the Streaming Locator has been created, you can get the streaming URLs. 若要生成 URL,需要连接 StreamingEndpoint 主机名称和流定位器路径 。To build a URL, you need to concatenate the StreamingEndpoint host name and the Streaming Locator path. 此示例使用默认的流式处理终结点In this sample, the default Streaming Endpoint is used. 首次创建媒体服务帐户时,此默认的流式处理终结点将处于停止状态,因此需要调用 StartWhen you first create a Media Service account, this default Streaming Endpoint will be in a stopped state, so you need to call Start.

private static async Task<string> GetDASHStreamingUrlAsync(
    IAzureMediaServicesClient client,
    string resourceGroupName,
    string accountName,
    string locatorName)
{
    const string DefaultStreamingEndpointName = "default";

    string dashPath = "";

    StreamingEndpoint streamingEndpoint = await client.StreamingEndpoints.GetAsync(resourceGroupName, accountName, DefaultStreamingEndpointName);

    if (streamingEndpoint != null)
    {
        if (streamingEndpoint.ResourceState != StreamingEndpointResourceState.Running)
        {
            await client.StreamingEndpoints.StartAsync(resourceGroupName, accountName, DefaultStreamingEndpointName);
        }
    }

    ListPathsResponse paths = await client.StreamingLocators.ListPathsAsync(resourceGroupName, accountName, locatorName);

    foreach (StreamingPath path in paths.StreamingPaths)
    {
        UriBuilder uriBuilder = new UriBuilder();
        uriBuilder.Scheme = "https";
        uriBuilder.Host = streamingEndpoint.HostName;

        // Look for just the DASH path and generate a URL for the Azure Media Player to playback the content with the AES token to decrypt.
        // Note that the JWT token is set to expire in 1 hour. 
        if (path.StreamingProtocol == StreamingPolicyStreamingProtocol.Dash)
        {
            uriBuilder.Path = path.Paths[0];

            dashPath = uriBuilder.ToString();

        }
    }

    return dashPath;
}

清理媒体服务帐户中的资源Clean up resources in your Media Services account

一般来说,除了打算重用的对象之外,应该清除所有对象(通常,将重用转换、流式处理定位符等)。Generally, you should clean up everything except objects that you're planning to reuse (typically, you'll reuse Transforms, Streaming Locators, and so on). 如果希望帐户在试验后保持干净状态,则删除不打算重复使用的资源。If you want for your account to be clean after experimenting, delete the resources that you don't plan to reuse. 例如,以下代码可删除作业:For example, the following code deletes Jobs:

private static async Task CleanUpAsync(
    IAzureMediaServicesClient client,
    string resourceGroupName,
    string accountName,
    string transformName,
    string contentKeyPolicyName)
{

    var jobs = await client.Jobs.ListAsync(resourceGroupName, accountName, transformName);
    foreach (var job in jobs)
    {
        await client.Jobs.DeleteAsync(resourceGroupName, accountName, transformName, job.Name);
    }

    var assets = await client.Assets.ListAsync(resourceGroupName, accountName);
    foreach (var asset in assets)
    {
        await client.Assets.DeleteAsync(resourceGroupName, accountName, asset.Name);
    }

    client.ContentKeyPolicies.Delete(resourceGroupName, accountName, contentKeyPolicyName);

}

清理资源Clean up resources

如果不再需要资源组中的任何一个资源(包括为本教程创建的媒体服务和存储帐户),请删除之前创建的资源组。If you no longer need any of the resources in your resource group, including the Media Services and storage accounts you created for this tutorial, delete the resource group you created earlier.

执行以下 CLI 命令:Execute the following CLI command:

az group delete --name amsResourceGroup

后续步骤Next steps