AWS generates a KeyID automatically but it is hard to learn this KeyID. Learn how to create a proxy between AWS and Axinom Key Service to read the value of the generated KeyID.

SPEKE Requests keyId Extraction

Context

AWS Media Services, specifically MediaConvert, and MediaPackage, support integration of a 3rd party Key Service using the key acquisition protocol called SPEKE. Moreover, MediaLive supports SPEKE in an indirect way by sending data to MediaPackage. Axinom Key Service can be used for this purpose, as it fully supports the SPEKE protocol. For integration, an AWS user needs the Key Service URL, the Tenant Id, and the Management Key. This data is available on Axinom Portal under My DRM.

Problem

On the AWS side, the user can configure a so-called “resourceId” for each video. This will be passed to the Key Service inside the CPIX structure as "id". However, the kid (keyId) is generated by AWS automatically. It does depend on the resourceId, but cannot be derived from the resourceId deterministically.

<cpix:CPIX xmlns:cpix="urn:dashif:org:cpix" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:speke="urn:aws:amazon:com:speke" id="test_ch1">
    <cpix:ContentKeyList>
        <cpix:ContentKey kid="af1ed63c-5784-460b-9e51-309dd47b7d9c"/>
    </cpix:ContentKeyList>
    ...

The Key Service receives the CPIX file and generates the content keys based on the keyId.

It is NOT possible for the AWS user to access the kid (keyId). The user needs the keyId to be able to implement an Entitlement Service, which generates an Entitlement Message approving specific keyIds when asked for a given video identified by the Id/resourceId.

A possible workaround is to analyze the resulting encrypted video and extract the keyId from the manifest file. But this is somewhat complicated and can only be done after the whole encoding process has finished.

Workaround: Proxy

A possible workaround is to use a Proxy between AWS Media Services and the Key Service.

AWS Media Services shall call the Proxy. The route for the request sent via Proxy is the following: AWS Media Services → API Gateway → Lambda → Key Service. Additionally, the Proxy shall extract the resourceId and the keyId from the request and store them. The stored pairs of the resourceId and keyId can be used by the Entitlement Service or other components that need to know the binding between the resourceId and the keyId.

AWS packager → API Gateway → Lambda → KS

AWS_Media_ServicesKey_ServiceAPI_GatewayProxyKey_Acquisition_APIDBRequest Content Key (CPIX)Content Key (CPIX)Request Content Key (CPIX)Content Key (CPIX)resourceId, keyId
Figure 1. Proxy between AWS Media Services and Key Service

The simplest way to run such a proxy is to use an AWS Lambda.

Here is a sample for such a proxy (simplified implementation):

Sample Proxy implementation using Python
import json
import requests
from xml.dom import minidom

def lambda_handler(event, context):
    requestBody = event['body']

    #Filter the values of resourceId and keyId
    xmldoc = minidom.parseString(requestBody)

    for element in xmldoc.getElementsByTagName('cpix:CPIX'):
        resourceId = element.attributes['id'].value
        print("Resource ID :", resourceId)

    for element in xmldoc.getElementsByTagName('cpix:ContentKey'):
        kid = element.attributes['kid'].value
        print("keyId: ", kid)


    #POST the request to SPEKE endpoint
    spekeEndpoint = 'https://key-server-management.axtest.net/api/Speke'
    authorizationHeader = "" # Basic base64("TenantID:ManagementKey")
    headers = {
    'Authorization': authorizationHeader,
    'Content-Type': 'text/xml'
    }

    response = requests.request("POST", spekeEndpoint, headers=headers, data=requestBody)

    #Pass through the response
    return_value = {
    "statusCode": response.status_code,
    "headers": response.headers,
    "body": response.text
    }

    return return_value

Such code can be deployed to AWS as a Lambda function. Then you can create an endpoint in an API Gateway pointing to this Lambda and supply this endpoint to Media Services and the endpoint for key acquisition.

The code assumes that the submitted request body contains a valid CPIX document. It extracts the resourceId (element cpix:CPIX, attribute id) and the keyId (element cpix:ContentKey, attribute kid). It prints both to the console. Then it simply forwards the same body to the real Key Service and passes the Key Service’s response to the caller.

For a real-life use, consider the following:

  • Instead of printing the resourceId and the keyId to the console, store them in some database (or deliver them in some other way to a location available for the Entitlement Service)

  • Instead of storing your Key Service credentials in the source code, use a more secure location, such as AWS Secret Manager

  • Add error handling

  • The code assumes there can be multiple resourceIds and keyIds. However, there will be only a single cpix:CPIX element, and SPEKE 1.0 only sends a single keyId.

Solution

Amazon is working on the next version of the SPEKE protocol - SPEKE 2.0. Axinom is committed to support SPEKE 2.0 as soon as it is published. SPEKE 2.0 should support some more advanced features, such as requesting multiple keys within a single request.

Axinom plans to implement an extension which allows to override the requested keyId server-side using a deterministic known algorithm. With this, there will be no need for a workaround, as the keyId will be known in advance given the resourceId.

Revision History

The table below outlines the document versions and any changes between them.

Version Date Description

1.0

March 1, 2021

  • Initial version.

1.1

September 8, 2021

  • Reference to another workaround: SPEKE v.10 Override.