SPEKE Key ID Extraction
Contextβ
SPEKE (Secure Packager and Encoder Key Exchange) is an open protocol defined by AWS that allows packagers to acquire encryption keys from a DRM key service. It is based on CPIX, which means, a caller would create an XML template describing all the necessary keys, and assigning a Key ID (GUID) to each key and submit this template to the key service. The key service inserts the generated keys into the same document and returns the augmented document back. The packager then uses the keys to encrypt the content.
Many packagers these days decide to use SPEKE for key acquisition.
Problemβ
In an attempt to make the process as simple as possible for the users, some packagers treat the Key ID as an internal detail and do not expose it to the users. Users, however, need the value of the Key IDs so that later an entitlement can be granted for an end user to access a specific Key ID.
The Key ID can later be extracted from a generated manifest file, but this is sometimes inconvenient and can only be done after packaging is completed.
The problem is especially severe, as the AWS Elemental services including (MediaConvert and MediaPackage) do not expose the Key ID.
Alternative Solutionsβ
Instead of changing the Key ID in the Key Service, one could set up a proxy between the packager and the Key Service that would intercept the requests and store the Key ID before it is forwarded to the Key Service.
Solution: Proxyβ
A possible solution 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
The simplest way to run such a proxy is to use an AWS Lambda.
Here is a sample for such a proxy (simplified implementation):
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.axprod.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 thekeyId
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
andkeyIds
. However, there will be only a singlecpix:CPIX
element, and SPEKE 1.0 only sends a singlekeyId
.
Alternative Solutionβ
Instead of intercepting the Key ID generated by the package, one can instruct the Key Service to ignore the received Key ID and to generate a new Key ID according to a known published algorithm. Then users can compute the Key IDs themselves.
See SPEKE Key ID Override Functionality.