使用 AES-128 动态加密和密钥传递服务

借助媒体服务,可以传送使用 AES 通过 128 位加密密钥加密的 HTTP Live Streaming (HLS)、MPEG-DASH 和平滑流。 媒体服务还提供密钥传送服务,将加密密钥传送给已授权的用户。 如果希望媒体服务来动态加密你的视频,您将加密密钥与流式处理定位符相关联,并还配置内容密钥的策略。 当播放器请求流时,媒体服务将使用指定的密钥来动态加密使用 AES-128 内容。 为解密流,播放器从密钥传送服务请求密钥。 为了确定是否已授权用户获取密钥,服务将评估你为密钥指定的内容密钥策略。

可以使用多个加密类型(AES-128、PlayReady、Widevine、FairPlay)来加密每个资产。 请参阅流式处理协议和加密类型,以了解有效的组合方式。 此外,请参阅如何使用 DRM 进行保护

该示例的输出这篇文章包括指向 Azure 媒体播放器的 URL、 清单 URL 和播放内容所需的 AES 令牌。 该示例将 JWT 令牌的过期时间设置为 1 小时。 可以打开浏览器并粘贴生成的 URL 来启动 Azure Media Player 演示页,其中已经填充了该 URL 和令牌(采用以下格式:https://ampdemo.azureedge.net/?url= {dash Manifest URL} &aes=true&aestoken=Bearer%3D{ JWT Token here})。

本教程演示如何:

  • 下载EncryptWithAES本文所述的示例
  • 开始结合使用媒体服务 API 与 .NET SDK
  • 创建输出资产
  • 创建编码的转换
  • 提交作业
  • 等待作业完成
  • 创建内容密钥策略
  • 将策略配置为使用 JWT 令牌限制
  • 创建流定位符
  • 配置流式处理定位符来加密使用 AES (ClearKey) 视频
  • 获取测试令牌
  • 生成流 URL
  • 清理资源

如果还没有 Azure 订阅,可以在开始前创建一个免费帐户

必备组件

以下是完成本教程所需具备的条件。

下载代码

使用以下命令,将包含本文中所述完整 .NET 示例的 GitHub 存储库克隆到计算机:

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

“使用 AES-128 加密”示例位于 EncryptWithAES 文件夹中。

 备注

每次运行应用程序时,该示例就将创建唯一的资源。 通常,你会重复使用现有的资源,例如转换和策略(如果现有资源具有所需的配置)。

开始结合使用媒体服务 API 与 .NET SDK

若要开始将媒体服务 API 与 .NET 结合使用,需要创建 AzureMediaServicesClient 对象。 若要创建对象,需要提供客户端所需凭据以使用 Azure AD 连接到 Azure。 在本文开头克隆的代码中,GetCredentialsAsync 函数根据本地配置文件中提供的凭据创建 ServiceClientCredentials 对象。

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

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

创建输出资产

输出资产会存储作业编码的结果。

C#
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);
}

获取或创建编码转换

创建新转换实例时,需要指定希望生成的输出内容。 所需参数是 TransformOutput 对象,如以下代码所示。 每个 TransformOutput 包含一个预设。 预设介绍了视频和/或音频处理操作的分步说明,这些操作将用于生成所需的 TransformOutput。 本文中的示例使用名为 AdaptiveStreaming 的内置预设。 此预设将输入的视频编码为基于输入的分辨率和比特率自动生成的比特率阶梯(比特率 - 分辨率对),并通过与每个比特率 - 分辨率对相对应的 H.264 视频和 AAC 音频生成 ISO MP4 文件。

在创建新的转换之前,应该先检查是否已存在使用 Get 方法的转换,如以下代码中所示。 在 Media Services v3获取实体上的方法返回null如果实体不存在 (不区分大小写的名称检查)。

C#
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;
}

提交作业

如上所述,转换对象为脚本,作业则是对媒体服务的实际请求,请求将转换应用到给定输入视频或音频内容。 作业指定输入视频位置和输出位置等信息。

本教程基于直接从 HTTPs 源 URL 引入的文件创建作业的输入。

C#
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;
}

等待作业完成

此作业需要一些时间才能完成,完成时可发出通知。 以下代码示例显示如何轮询服务以获取作业状态。 对于生产应用程序,由于可能出现延迟,并不建议将轮询作为最佳做法。 如果在帐户上过度使用轮询,轮询会受到限制。 开发者应改用事件网格。 请参阅将事件路由到自定义 Web 终结点

作业通常会经历以下状态:已计划、已排队、正在处理、已完成(最终状态)。 如果作业出错,则显示“错误”状态。 如果作业正处于取消过程中,则显示“正在取消”,完成时则显示“已取消”。

C#
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;
}

创建内容密钥策略

内容密钥提供对资产的安全访问。 您需要创建内容密钥策略用于配置内容密钥传递的方式来结束客户端。 与关联的内容密钥流式处理定位符。 媒体服务还提供密钥传送服务,将加密密钥传送给已授权的用户。

当播放器请求流时,媒体服务将使用指定的密钥通过 AES 加密来动态加密内容(本例中使用 AES 加密)。为解密流,播放器从密钥传送服务请求密钥。 为了确定是否已授权用户获取密钥,服务将评估你为密钥指定的内容密钥策略。

C#
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;
}

创建流定位符

完成编码并设置内容密钥策略后,下一步是使输出资产中的视频可供客户端播放。 通过两个步骤实现此目的:

  1. 创建流式处理定位符
  2. 生成客户端可以使用的流式处理 URL。

创建的过程流式处理定位符称为发布。 默认情况下,除非配置可选的开始和结束时间,否则调用 API 后,流定位符立即生效,并持续到被删除为止。

创建流式处理定位符时,需要指定所需的 StreamingPolicyName。 本教程使用某个 PredefinedStreamingPolicies 来告知 Azure 媒体服务如何发布流式处理的内容。 此示例中应用了 AES 信封加密(也称为 ClearKey 加密,因为密钥是通过 HTTPS 而不是 DRM 许可证传送到播放客户端的)。

 重要

使用自定义的 StreamingPolicy 时,应为媒体服务帐户设计有限的一组此类策略,并在需要同样的加密选项和协议时重新将这些策略用于流式处理定位符。 媒体服务帐户具有对应于 StreamingPolicy 条目数的配额。 您应该不需要创建新 StreamingPolicy 为每个流式处理定位符。

C#
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;
}

获取测试令牌

本教程在内容密钥策略中指定使用令牌限制。 令牌限制策略必须附带由安全令牌服务 (STS) 颁发的令牌。 媒体服务支持采用 JSON Web 令牌 (JWT) 格式的令牌,我们在示例中配置了此格式。

在中使用 ContentKeyIdentifierClaim内容密钥策略,这意味着提供给密钥传送服务的令牌,必须具有在其中的内容密钥的标识符。 在示例中,我们并未指定内容密钥创建流式处理定位符时,系统将创建一个随机的。 若要生成测试令牌,必须获取要放入 ContentKeyIdentifierClaim 声明中的 ContentKeyId。

C#
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 流 URL

既然流式处理定位符已创建,可以获取流 Url。 若要生成的 URL,您需要串联StreamingEndpoint主机名和流式处理定位符路径。 此示例使用默认的流式处理终结点。 首次创建媒体服务帐户时,默认的流式处理终结点处于停止状态,因此需要调用 Start。

C#
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;
}

清理媒体服务帐户中的资源

通常情况下,您应清除想要重复使用的对象以外的所有内容 (通常情况下,将重复使用转换,并且您将会保留流式处理定位符,等等。)。 如果希望帐户在试验后保持干净状态,则应删除不打算重复使用的资源。 例如,以下代码可删除作业。

C#
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);

}

清理资源

如果不再需要资源组中的任何一个资源(包括为本教程创建的媒体服务和存储帐户),请删除之前创建的资源组。

执行以下 CLI 命令:

Azure CLI
az group delete --name amsResourceGroup

提出问题、 提供反馈,获取更新

查看 Azure 媒体服务社区文章,了解可以提出问题、提供反馈和获取有关媒体服务的更新的不同方法。

posted on 2019-05-10 01:39  &大飞  阅读(4795)  评论(0编辑  收藏  举报

导航