License Token Tutorial

Overview

This document provides step-by-step instructions on how to generate tokens for the token-based license issuance, along with sample code.

Notice

Before you read this tutorial, please keep the following points in mind:

Where tokens are created

  • The creation of the token must be performed on the Content Management System (CMS) back-end server of the content service to which PallyCon Multi-DRM will be applied.
  • Generating tokens directly from within client-side JavaScript or mobile applications is a security risk and should be avoided.

How to use the created token

  • Before playing the DRM content on the client player, the CMS backend must determine whether the client or user is authorized to play. Then it generates a token with DRM license rules (time limit and security options) and pass it to the client.
  • The function of delivering the token data to the client should be implemented by the content service. Usually it is transmitted with the metadata (playback URL, DRM linked data, poster image, etc.) of the DRM contents to be played.

Sample codes and inputs

  • The sample code in this document is based on Node.JS. If you use any other server-side programming language, please refer to the sample code and implement the converted code yourself.
  • The various setting values shown in the sample code are for reference only. The generated token data in the sample code does not actually work.
  • To create a working token, you need to enter the setting value of your account after signing up to PallyCon service.

The step-by-step example code below can be run in a NodeJS application or in an online NodeJS IDE like JDoodle.

Step 1 - Input setting values

Prepare various input values required for license token generation as follows.

const AES_IV = "0123456789abcdef";
const siteInfo = {
  "siteId": "TUTO",
  "siteKey": "lU5D8s3PWoLls3PWFWkClULlFWk5D8oC",
  "accessKey": "LT2FVJDp2Xr018zf4Di6lzvNOv3DKP20"
};

let licenseInfo = {
  "drmType": "Widevine",
  "contentId": "bigbuckbunny",
  "userId": "test-user"
};

let licensePolicy = {
  "policy_version": 2,
  "playback_policy": {
    "persistent": false
  }
};

console.log("license policy : " + JSON.stringify(licensePolicy));

Input value description

Name Format Description
AES_IV 16 bytes string “0123456789abcdef” (fixed value)
siteId 4 uppercase alphabet The ID of the service site registered on PallyCon Console site (check the PallyCon Console’s ‘Site Settings’ page)
siteKey 32 bytes base64 string Encryption key required for service site integration (check the PallyCon Console’s ‘Site Settings’ page)
accessKey 32 bytes base64 string Encryption key used for additional integration such as packager, license token, etc. (check the PallyCon Console’s ‘Site Settings’ page)
drmType Input one of these DRM. “NCG”, “Widevine”, “PlayReady”, “FairPlay” (case sensitive) Enter the type of DRM corresponding to the client environment. Refer to DRM Platform Support
contentId Max 200 alphanumeric characters The unique ID of the DRM content to play. Must be the same as the CID used to package the content.
userId Max 32 alphanumeric characters End-user’s ID which is managed by the content service. Input “LICENSETOKEN” if there is no user ID.

Value of drmType should be entered exactly in case according to the specification.

LicensePolicy JSON data is used to set the detailed policy for the content playback rights. This tutorial uses the default values as entered in the sample code above.

When you run Step 1 code, the following results are output to the browser console.

license policy : {"policy_version":2,playback_policy:{persistent:false}}

Step 2 - Encrypt license rule

Encrypt the license rules JSON string prepared in step 1 with AES256 algorithm and convert the result to a base64 string.

// Add the following code after step 1 code

const crypto = require("crypto");

const cipher = crypto.createCipheriv("aes-256-cbc", siteInfo.siteKey, AES_IV);
let encryptedPolicy = cipher.update(
  JSON.stringify(licensePolicy),
  "utf-8",
  "base64"
);
encryptedPolicy += cipher.final("base64");

console.log("encrypted policy : " + encryptedPolicy);

The following settings are used for AES encryption:

  • Encryption algorithm: AES256 CBC mode
  • Encryption key: The 32-byte PallyCon site key value entered in step 1
  • IV (Initial Vector): 16 byte AES_IV value entered in Step 1
  • Padding: pkcs7 (not shown in the above code because it is default)

AES encryption processing must use the appropriate libraries and APIs for each server-side programming language.

The execution result of the second stage code is as follows.

encrypted policy : sX7DEMZ1S2xaMoIt76m+yDwylZRVJ2Gzs2I1b0+mzL0X9FcJ43aAloqI5noG2iyC57AagPl2jY9mxCL/WqNabQ==

Step 3 - Create hash value

The hash value used to verify the integrity of the token data is generated as follows:

// Add the following code after step 2 code

const UTCTime = new Date().toISOString().replace(/\.\d{3}/gi, '');

let hashData = {
  siteId: siteInfo.siteId,
  accessKey: siteInfo.accessKey,
  drmType: licenseInfo.drmType,
  userId: licenseInfo.userId,
  cid: licenseInfo.contentId,
  policy: encryptedPolicy,
  timestamp: UTCTime
};

const hashInput =
  hashData.accessKey +
  hashData.drmType +
  hashData.siteId +
  hashData.userId +
  hashData.cid +
  hashData.policy +
  hashData.timestamp;

console.log("hash input : " + hashInput);

let hashString = crypto
  .createHash("sha256")
  .update(hashInput)
  .digest("base64");

console.log("hash string : " + hashString);

The input value for generating a hash is a string of various configuration values, encrypted license rules, and current time information according to token specification.

The SHA256 algorithm is used to generate hash values, and the resulting value of the hash function must be entered into the base64 function as a binary data, without being converted to a string.

The result of the execution of Step 3 code is as follows.

hash input : LT2FVJDp2Xr018zf4Di6lzvNOv3DKP20WidevineTUTOtest-userbigbuckbunnysX7DEMZ1S2xaMoIt76m+yDwylZRVJ2Gzs2I1b0+mzL0X9FcJ43aAloqI5noG2iyC57AagPl2jY9mxCL/WqNabQ==2021-11-11T07:28:41Z

hash string : o6dD1qJbdrQvU86JUxZiKf8WBo0BH6oc+kN8CVNk2rM=

Step 4 - Create license token

Using the data prepared so far, create the license token as follows.

// Add the following code after step 3 code

let tokenData = {
  "drm_type": licenseInfo.drmType,
  "site_id": siteInfo.siteId,
  "user_id": licenseInfo.userId,
  "cid": licenseInfo.contentId,
  "policy": encryptedPolicy,
  "timestamp": UTCTime,
  "hash": hashString, 
  "response_format": "original",
  "key_rotation": false
};

console.log("token json : " + JSON.stringify(tokenData));

let base64Token = Buffer.from(JSON.stringify(tokenData)).toString("base64");

console.log("base64 encoded token : " + base64Token);

First, JSON data is created by combining various input values and encrypted license rules, timestamps, and hash values according to the token specification. Converting the corresponding JSON data to a Base64 string completes the token for the client to use for DRM license requests.

The result of executing the 4th step code is as follows.

token json : {"drm_type":"Widevine","site_id":"TUTO","user_id":"test-user","cid":"bigbuckbunny","policy":"sX7DEMZ1S2xaMoIt76m+yDwylZRVJ2Gzs2I1b0+mzL0X9FcJ43aAloqI5noG2iyC57AagPl2jY9mxCL/WqNabQ==","timestamp":"2021-11-11T07:28:41Z","hash":"o6dD1qJbdrQvU86JUxZiKf8WBo0BH6oc+kN8CVNk2rM=","response_format":"original","key_rotation":false}

base64 encoded token : eyJkcm1fdHlwZSI6IldpZGV2aW5lIiwic2l0ZV9pZCI6IlRVVE8iLCJ1c2VyX2lkIjoidGVzdC11c2VyIiwiY2lkIjoiYmlnYnVja2J1bm55IiwicG9saWN5Ijoic1g3REVNWjFTMnhhTW9JdDc2bSt5RHd5bFpSVkoyR3pzMkkxYjArbXpMMFg5RmNKNDNhQWxvcUk1bm9HMml5QzU3QWFnUGwyalk5bXhDTC9XcU5hYlE9PSIsInRpbWVzdGFtcCI6IjIwMjEtMTEtMTFUMDc6Mjg6NDFaIiwiaGFzaCI6Im82ZEQxcUpiZHJRdlU4NkpVeFppS2Y4V0JvMEJINm9jK2tOOENWTmsyck09IiwicmVzcG9uc2VfZm9ybWF0Ijoib3JpZ2luYWwiLCJrZXlfcm90YXRpb24iOmZhbHNlfQ==

This completes the license token tutorial.

To play the actual DRM content using the created token, please refer to the Bitmovin HTML5 Player Tutorial posted on the PallyCon blog site.

Previous
Next