Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Current »

概要

KollusモバイルダウンロードDRMを利用してダウンロードしたコンテンツの「再生回数」、「再生有効期限」、「再生時間」を設定してコンテンツの再生を制御する機能について説明する文書です。サービス提供形態によって重複制限の設定として使用されることもあります。

Expire option

設定項目のdata-typeや値が範囲から外れた場合、エンドユーザーのコンテンツの利用に問題が発生する可能性があります。また、誤った設定値による使用回数の過剰などを回収する方法はありませんので設定に注意してください。

  • Expire count : 再生回数の制限

    • data-type : integer
    • range : 0 ~ 1000
    • 0 : unlimited (無制限)
  • Expire date : 有効期限の制限

    • data-type : integer, unixtime stamp

    • コンテンツの有効期限(再生可能期限)

      • 終了日(日時)
        例) 2014. 3. 3.  5時 45分 30秒 GMT → 1393825531

      • 0 : unlimited (無制限)

      • 最大値 : 2029年 12月 31日 23時 59分 59秒 (1893455999)

  • Expire playtime : 再生時間(倍速再生の場合は倍速が適用された時間で計算)

    • data-type : integer
    • range : 0, 60 ~ 604800 (単位: 秒, min : 60秒, max:1週間)
    • 0 : unlimited (無制限)

DRM callback

DRM ポリシーを適用するためには、チャンネルにDRM Callback URLを設定する必要があります。

チャンネルにDRM Callback URLを設定すると、コンテンツをダウンロードする時点にDRM Callback URLを呼び出して、リターン値をDRMポリシーとして使用します。ダウンロードされるDRMポリシー情報はJWT EncodeされてHttp Bodyにリターンしなければなりません。

注意:

  1. DRM Callback URLがレスポンスしないとダウンロードできません。
  2. アルゴリズムはHS256のみ対応しており、Httpヘッダに指定された(X-KOLLUS-USERKEY)ヘッダに“ユーザーキー”を共に転送しなければなりません。

Callback ​flow

  1. 配信チャンネルに DRM CallbackURLを設定します。
  2. JSONデータを生成してJWTでエンコードする
  3. Kollus mobile playerがhttp://www.foo.com/auth.php に以下の情報をPOST転送します。
    • session_key : Playerが生成したリクエスト確認用のセッションキー
      • kind3のcontent_expire_resetの場合、リクエストしたsession_keyを確認します。
      • Callback v2が適用されるv1.6以降から使用可能です。
    • kind : 1 - 3
    • client_user_id : ユーザID(サービス会員情報)
      • Media token 生成の際に含まれたID
    • player_id : ユーザのデバイスID
    • device_name : ユーザのデバイス名
      • Android, iOS別に転送される端末名が異なります。
      • iOS:事前にApple社が定義した文字列で転送されます。
      • Android:デバイス名、モデル名が転送されます。※該当する情報がない場合NULLで転送
    • media_content_key : ダウンロードするコンテンツキー
      • チャンネルに登録されたコンテンツのメディアコンテンツキー(ユニーク)
      • 同じコンテンツを複数のチャンネルに登録する場合、それぞれのmedia_content_keyは全て異なります。
  4. 顧客のDRM認証サーバーは転送された上記の情報に基づいて以下のjson フォーマットのdataをJWT payloadに追加してEncoding します。ヘッダーに指定された“ユーザキー(X-KOLLUS-USERKEY)"を一緒に転送します。
    (顧客が認証データをPlayerに転送する全てのデータは必ずinteger型で転送しなければなりません。)

device_name 追加説明

  • Andoid

    Androidアプリケーションの開発に使用される Build.DEVICE, Build.MODELは /(スラッシュ)で区分した文字列を作成して使用してください。

    • Build.DEVICE+”/”+Build.MODEL
    • デバイスによって該当情報はNULLで表示される可能性があります。
  • iOS

    iOSから提供するdevice-nameを使用します。

Device TypeProduct Name
iPhone1,1iPhone
iPhone1,2iPhone 3G
iPhone2,1iPhone 3GS
iPhone3,1iPhone 4 (GSM)
iPhone3,3iPhone 4 (CDMA)
iPhone4,1iPhone 4S
iPhone5,1iPhone 5 (A1428)
iPhone5,2iPhone 5 (A1429)
iPhone5,3iPhone 5c (A1456/A1532)
iPhone5,4iPhone 5c (A1507/A1516/A1529)
iPhone6,1iPhone 5s (A1433/A1453)
iPhone6,2iPhone 5s (A1457/A1518/A1530)
iPhone7,1iPhone 6 Plus
iPhone7,2iPhone 6
iPhone8,1iPhone 6s
iPhone8,2iPhone 6s Plus
iPhone8,4iPhone SE
iPhone9,1iPhone 7 (A1660/A1779/A1780)
iPhone9,2iPhone 7 Plus (A1661/A1785/A1786)
iPhone9,3iPhone 7 (A1778)
iPhone9,4iPhone 7 Plus (A1784)
iPhone10,1iPhone 8 (A1863/A1906)
iPhone10,2iPhone 8 Plus (A1864/A1898)
iPhone10,3iPhone X (A1865/A1902)
iPhone10,4iPhone 8 (A1905)
iPhone10,5iPhone 8 Plus (A1897)
iPhone10,6iPhone X (A1901)
iPad1,1iPad
iPad2,1iPad 2 (Wi-Fi)
iPad2,2iPad 2 (GSM)
iPad2,3iPad 2 (CDMA)
iPad2,4iPad 2 (Wi-Fi, revised)
iPad2,5iPad mini (Wi-Fi)
iPad2,6iPad mini (A1454)
iPad2,7iPad mini (A1455)
iPad3,1iPad (3rd gen, Wi-Fi)
iPad3,2iPad (3rd gen, Wi-Fi+LTE Verizon)
iPad3,3iPad (3rd gen, Wi-Fi+LTE AT&T)
iPad3,4iPad (4th gen, Wi-Fi)
iPad3,5iPad (4th gen, A1459)
iPad3,6iPad (4th gen, A1460)
iPad4,1iPad Air (Wi-Fi)
iPad4,2iPad Air (Wi-Fi+LTE)
iPad4,3iPad Air (Rev)
iPad4,4iPad mini 2 (Wi-Fi)
iPad4,5iPad mini 2 (Wi-Fi+LTE)
iPad4,6iPad mini 2 (Rev)
iPad4,7iPad mini 3 (Wi-Fi)
iPad4,8iPad mini 3 (A1600)
iPad4,9iPad mini 3 (A1601)
iPad5,1iPad mini 4 (Wi-Fi)
iPad5,2iPad mini 4 (Wi-Fi+LTE)
iPad5,3iPad Air 2 (Wi-Fi)
iPad5,4iPad Air 2 (Wi-Fi+LTE)
iPad6,3iPad Pro (9.7 inch) (Wi-Fi)
iPad6,4iPad Pro (9.7 inch) (Wi-Fi+LTE)
iPad6,7iPad Pro (12.9 inch, Wi-Fi)
iPad6,8iPad Pro (12.9 inch, Wi-Fi+LTE)
iPad6,11iPad 9.7-Inch 5th Gen (Wi-Fi Only)
iPad6,12iPad 9.7-Inch 5th Gen (Wi-Fi/Cellular)
iPad7,1iPad Pro (12.9 inch, A1670)
iPad7,2iPad Pro (12.9 inch, A18219)
iPad7,3iPad Pro (10.5 inch, A1701)
iPad7,4iPad Pro (10.5 inch, A1709)
iPad7,5iPad (6th gen, A1893)
iPad7,6iPad (6th gen, A1954)
iPod1,1iPod touch
iPod2,1iPod touch (2nd gen)
iPod3,1iPod touch (3rd gen)
iPod4,1iPod touch (4th gen)
iPod5,1iPod touch (5th gen)
iPod7,1iPod touch (6th gen)

DRM Callback 新規バージョン v2

バージョン1.4まで提供していた「kind1, kind2, kind3」の個別で呼び出しを行われたことを、一回の呼出で統合しました。ユーザー側の呼出が増えることで対応が難しいとの多数の顧客からの意見に対応しました。新しいバージョンを"v2"で、以前バージョンを"v1"で記載します。

Reuest

区分Description
POSTHttp POSTでリクエスト (parameterではない)
itemsJsonArrayで構成されたstring

items項目はkind1, kind2, kind3の全てのデータが含まれた呼出にすることができます。

items (JsonArray)
kind1, kind2
区分Description
kind1,2
client_user_idユーザーID, media_token 生成に使用されたclient_user_idと同一です。
player_idユーザーのデバイスID
hardware_idデバイスのhardware ID(PC, 内容がある場合)
device_nameユーザーデバイスのモデル名
media_content_keyKollusに登録されたコンテンツのメディアコンテンツキー
uservaluesJSON format (VideoGatewayの呼出に使用されたuservalue0~9)
localtimeデバイスの時刻 (UTC)
kind3
区分Description
kind3
session_keycontent_expire_resetリクエストの際に同じsession_keyを確認します。
client_user_idユーザーID, media_token 生成に使用されたclient_user_idと同一です。
player_idユーザのデバイスID
hardware_idデバイスのhardware ID(PC, 内容がある場合)
device_nameユーザーデバイスのモデル名
media_content_keyKollusに登録されたコンテンツのメディアコンテンツキー
start_atunixtimestamp (localtime)
- 転送リクエスト時刻
uservaluesJSON format (VideoGatewayの呼出に使用されたuservalue0~9)
content_expiredコンテンツの有効期限を確認 flag ( 1: 再生不可, 0: 再生可能)
check_expired更新チェックの有効期限を確認 flag ( 1: 再生不可, 0: 再生可能)
reset_req一括更新なのかを確認 ( 0 (default), 1: 一括更新)
expiration_date 有効期限終了日(unixtimestamp)
localtimeデバイスの時刻 (UTC)
Items Sample
[
    {
        "kind": 1,
        "media_content_key" : "XXX-MEDIA_CONTENTKEY-XXX",
        "client_user_id": "XXXXXXX",
        "player_id": "xxxxxxxxxxxxxxxx",
        "device_name": "XXXXX",
        "uservalues": {
            "uservalue0": "value0"
        }
    },
    {
        "kind": 2,
        "media_content_key" : "XXX-MEDIA_CONTENTKEY-XXX",
        "client_user_id": "XXXXXXX",
        "player_id": "xxxxxxxxxxxxxxxx",
        "device_name": "XXXXX",
        "uservalues": {
            "uservalue0": "value0"
        }
    },
    {
        "kind": 3,
        "session_key" : "XXX-SESSION_KEY-XXX", ← content_expire_reset リクエストする際に確認します。
        "media_content_key" : "XXX-MEDIA_CONTENTKEY-XXX",
        "client_user_id": "XXXXXXX",
        "player_id": "xxxxxxxxxxxxxxxx",
        "device_name": "XXXXX",
        "uservalues": {
       		"uservalue1": "value1"
        }
    }
]

Response

Arrayは“data”フィールドで受け取る

kind1
区分必須Description
(int) kindO1
(string) media_content_keyOKollusに登録されたコンテンツのメディアコンテンツキー
(int) expiration_date
有効期限のunixtime stamp
最大値 : 2029年 12月 31日 23時 59分 59秒 (1893455999)
(int) expiration_count
再生回数制限 例) 10 ← 10回まで再生可能
(int) expiration_playtime
再生時間制限 例) 3600 ← 3600秒(1時間)まで再生可能
(int) expiration_playtime_type
1の場合、play状態を起動時間として制限
(int) resultO0 (エラー), 1 (正常)
0の場合ダウンロードできません。
0の場合リトライしません。
(string) message
0 (エラー)の場合、message内容を追加するとエラーが起きた際に内容が表示されます。
(int)
expiration_refresh_popup

有効期限終了後、ポリシー更新表示の有無
0の場合表示しない (基本値)
1の場合有効期限終了後にユーザの確認を求めるメッセージを表示します。
(int) vmcheck
virtual machine 有無確認 (PC) 
0: 確認しない, 1: 確認する (基本値)
(int) check_abuse
DRM kind3 を常に確認 (0: 確認しない(基本値), 1: 確認する)
(int) offline_bookmark.download

オフライン状態でブックマークダウンロード使用有無
コンテンツをダウンロードした時点のデータのみ反映され、以降サーバーとのシンクはしない
0: しない(基本値)
1: ブックマークのみダウンロード

(int) offline_bookmark.readonly
オフライン状態でブックマーク追加/削除の使用有無 (0: 使用する(基本値), 1: 使用しない)
kind2
区分必須Description
(int) kindO2
(string) media_content_keyOコンテンツのメディアコンテンツキー
(int) content_delete
0 (削除しない)、1 (ダウンロードしたファイルを削除)
ダウンロードしたコンテンツを削除するオプションです。
(string) message
0 (エラー)の場合、またはcontent_deleteが1(ダウンロードしたファイルを削除)の場合にmessageを追加すると状況によるメッセージが表示されます。(内容編集可能)
(int) check_expiration_date
使用しない(0), 更新チェックの有効期限終了日のunixtime stamp
(int) resultO0 (エラー)、1 (正常)
0の場合、追加作業はありません。
0の場合、追加オプションは無視されます。
0の場合、再度リクエストされません。
kind3
区分必須Description
(int) kindO3
(string) session_key
content_expire_reset 요청시 session_key 확인합니다.
(string) media_content_keyOKollus 컨텐츠 Unique Key
(int) start_atORequest에 포함된 start_at
(int) content_expired
0 (재생가능), 1 (재생 제한)
다운로드 컨텐츠를 강제로 expire 시킵니다.
필요시 content_expire_reset 옵션으로 다시 복구 할 수 있습니다.
* expired 컨텐츠는 0(재생가능)으로 응답해도 재생되지 않습니다.
* 1인 경우 content_expire_reset 무시됩니다.
(int) content_delete
0 (삭제하지 않음), 1 (다운로드 받은 파일 삭제)
다운로드된 컨텐츠를 요청에 의해 삭제하는 옵션입니다.
content_expired 옵션과 상관 관계없이 해당 옵션이 존재하는 경우 삭제합니다.
(int) content_expire_reset
0 (추가 액션 없음), 1 (expired된 콘텐츠 권한 Reset)
expired된 콘텐츠 권한을 Reset 하는 옵션입니다.
(int) expiration_date
만료될 시간의 unixtime stamp
최대값 : 2029년 12월 31일 23시 59분 59초 (1893455999)
* content_expire_reset 옵션이 1일때 재설정 됩니다.
(int) expiration_count
재생 제한 횟수, 예) 10 ← 10번 재생 가능
* content_expire_reset 옵션이 1일때 재설정 됩니다.
(int) expiration_playtime
재생 시간 제한, 예) 3600 ← 1시간(3600초) 재생 가능
* content_expire_reset 옵션이 1일때 재설정 됩니다.
(int) resultO0 (비정상), 1 (정상)
0 인 경우 재생되지 않습니다.
0 인 경우 추가 옵션은 무시됩니다.
0 인 경우 재 요청되지 않습니다.
(string) message
0 (비정상), content_expired이 1(재생 제한) 또는 content_delete이 1(다운로드 받은 파일 삭제) 시 message를 추가하면 상황에 따른 메시지가 표시됩니다.
(int) check_abuse
DRM kind3 항상 물어보기(0 : 안함(기본값), 1: 체크)
(int) check_expiration_date

사용안함(0), 체크 유효 만료될 시간의 unixtime stamp

* content_expire_reset 옵션이 1일때 재설정 됩니다.
Response ​Example
{
    “data” : [
        {
            "kind": 1,
            "media_content_key": "XXX-MEDIA_CONTENT_KEY-XXX",
            "expiration_date": 1402444800,
            "expiration_count": 10,
            "expiration_playtime": 60,
            "result": 1
        },
        {
            "kind": 2,
            "media_content_key": "XXX-MEDIA_CONTENT_KEY-XXX",
            "content_delete": 1,
            "result": 1
        },
        {
            "kind": 3,
            "session_key" : "XXX-SESSION_KEY-XXX",
            "media_content_key": "XXX-MEDIA_CONTENT_KEY-XXX",
            "start_at": 140000000,
            "content_expired": 1,
            "content_delete": 1,
            "content_expire_reset": 1,
            "expiration_date": 1402444800,
            "expiration_count": 10,
            "expiration_playtime": 3600,
            "result": 1
        }
    ]
}

JWT 지원

DRM 정책을 JWT로 반환하는 것을 지원합니다. JWT 웹사이트(https://jwt.io) 에서 공인한 라이브러리를 이용하여 생성된 Token으로 변경하여 반환하도록 추가 되었습니다.

플레이어에 JWT Token으로 반환하는 경우에 지정된 헤더에 사용자키를 포함 시켜 전송해야 합니다.

HTTP/1.1 200 OK
Date: Fri, 14 Oct 2016 04:12:46 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Kollus-UserKey: 0993d76eb424a72f2005b874ac49405d44a6c
Content-Encoding: gzip

지정된(X-Kollus-UserKey) 해더로 전송된 사용자키가 일치 하는 경우 플레이어에서 정상적으로 동작하게 됩니다.

JWT ​Secret,​ ​JWT​ ​사용자​ ​키

기존 사용자 키(2)키를 header에 추가하는 것 외에 추가적으로 기존의 JWT scpec에 명시되어 있는 secret key는 보안키(1)​를 ​입력해야 ​해야 ​합니다. ​고객사는 ​관련 ​정보를 ​아래와 ​같이 Kollus CMS 에서 확인 할 수 있습니다.

설정 > 서비스 계정 설정


JWT payload

각 callback의 response를 JWT playload에 담아 전송합니다.

Sample

http://jwt.io 에서 제공하는 다양한 언어로 구현된 라이브러리와 예제를 이용해 구현하도록 합니다. JWT ​스펙과 ​동일하나 ​반환되는 http ​헤더에 X-Kollus-UserKey​를 ​이용하여 ​사용자키를 함께 전송해주셔야 합니다.

(예시) php

define('KOLLUS_KEY', '**사용자키**'); // X-Kollus-UserKey
define('JWT_KEY', '**보안키**'); // JWT 보안 키

function encode_jwt($payload, $key, $alg = 'HS256') {
    $header = array('alg' => $alg, 'typ' => 'JWT' );
    $unsignedToken = base64_encode(json_encode($header)).'.'.
    base64_encode(json_encode($payload));
    $signature = hash_hmac('sha256', $unsignedToken, $key, TRUE);
    return $unsignedToken.'.'.base64_encode($signature);
}
$payload = array(
    'data' => array(
        array(
            'kind' => 1,
            'result' =>1,
            'expiration_date' => time()+3600,
            'expiration_playtime' => 30,
            'vmcheck' => 1,
        ),
        array(
            'kind' => 2,
            'result' =>1,
            'expiration_date' => time()+3600,
            'expiration_playtime' => 30,
            'vmcheck' => 1,
        ),
        array(
            'kind' => 3,
            'result' =>1,
            'expiration_date' => time()+3600,
            'expiration_playtime' => 30,
            'vmcheck' => 1,
        ),
    ),
);

$result = encode_jwt($payload, JWT_KEY);

//header('Content-Type: application/jwt');
header('X-Kollus-UserKey: '.KOLLUS_KEY);
echo $result;

Code sample (v1.4 기준)

다음과 같은 고객사의 DB가 구성된 것으로 고려되어 샘플이 구성되어있습니다.

PHP ​sample

<?php
/**
* PHP Version : 5.4 above
*/
function get_jwt($payload, $key, $alg = 'HS256')
{
	$header = array('alg' => $alg, 'typ' => 'JWT' );
	$unsignedToken = base64_encode(json_encode($header)).'.'.base64_encode(json_encode($payload));
	$signature = hash_hmac('sha256', $unsignedToken, $key, TRUE);
	return $unsignedToken.'.'.base64_encode($signature);
}

function print_kollus_jwt($data)
{
    define('KOLLUS_KEY', '**사용자키**'); // X-Kollus-UserKey
    define('JWT_KEY', '**보안키**'); // JWT 보안 키
    $payload = array( ‘data’ => $data );
    $result = encode_jwt($payload, JWT_KEY);
    //header('Content-Type: application/jwt');
    header('X-Kollus-UserKey: '.KOLLUS_KEY);
    echo $result;
}

/////////////////////////////////////////////////

include "./config.php";
// 주의 : kind가 1일때 자동으로 expiration_date가 생성되는 샘플 페이지입니다.
// 재생 제한 횟수
$_default_expiration_count = 3;
$_expired_duration = 60 * 60 * 24; // 1 day

// DB Connection
$_db_conn = mysqli_connect($_hostname, $_username, $_password);
if (!$_db_conn) {
	die('Could not connect: ' . mysql_error());
}

$_db_selected = mysqli_select_db($_database, $_db_conn);
if (!$_db_selected) {
	die ('Can\'t use database : ' . mysqli_error());
}

$_kind = isset($_POST['kind']) ? ((int) $_POST['kind']) : NULL;
$_media_content_key = isset($_POST['media_content_key']) ? $_POST['media_content_key'] : NULL;
$_client_user_id = isset($_POST['client_user_id']) ? $_POST['client_user_id'] : NULL;

$channel = NULL;
$_query = sprintf("SELECT * FROM `channels` WHERE `media_content_key` = '%s'",
mysqli_real_escape_string($_media_content_key, $_db_conn));
$_result = mysqli_query($_query, $_db_conn);

if ($_result) {
	$channel = mysqli_fetch_array($_result, MYSQL_ASSOC);
	if ($channel === FALSE) $channel = NULL;
} else {
	die('Invalid query: ' . mysqli_error());
}

$user = NULL;
$_query = sprintf("SELECT * FROM `users` WHERE `client_user_id` = '%s'",
mysqli_real_escape_string($_client_user_id, $_db_conn));
$_result = mysqli_query($_query, $_db_conn);
if ($_result) {
	$user = mysqli_fetch_array($_result, MYSQL_ASSOC);
	if ($user === FALSE) $user = NULL;
} else {
	die('Invalid query: ' . mysqli_error());
}

$channel_user = NULL;
if (!is_null($_media_content_key) && !is_null($_client_user_id)) {
	$_query = sprintf("SELECT * FROM `channel_users` WHERE `user_id` = '%s', `channel_id` = '%s'", $user['id'], $channel['id']);
	$_result = mysqli_query($_query, $_db_conn);
	if ($_result) {
		$channel_user = mysqli_fetch_array($_result, MYSQL_ASSOC);
		if ($channel_user === FALSE) $channel_user = NULL;
	} else {
		die('Invalid query: ' . mysqli_error());
	}
}

$_jwt_result = array('result' => 0);
switch($_kind) {
case 1:
	if (is_null($channel_user)){
		$_expiration_date = time() + $_expired_duration;
        $_expiration_count = $_default_expiration_count;
        $_query = sprintf("INSERT INTO `channel_users`(`user_id`, `channel_id`, `expiration_date`
        ,`expiration_count` , `created_at`, `updated_at`) VALUES('%s', '%s', '%s', '%s', UNIX_TIMESTAMP(),
        UNIX_TIMESTAMP())", $user['id'], $channel['id'], $_expiration_date, $_expiration_count);
        $_result = mysqli_query($_query, $_db_conn);
		if (!$_result) {
			die('Invalid query: ' . mysqli_error());
		}
	} else {
        $_expiration_date = $channel_user['expiration_date'];
        $_expiration_count = $channel_user['$expiration_count'];
	}
    $_jwt_result['expiration_date'] = (int) $_expiration_date;
    $_jwt_result['expiration_count'] = (int) $_expiration_count;
	break;
case 2:
    if (!is_null($channel_user)){
        $_download_times = ++((int) $channel_user['download_times']);
        $_query = sprintf("UPDATE `channel_users` SET `download_times` = '%s', `updated_at` =
        UNIX_TIMESTAMP() WHERE id = %s", $_download_times, $channel_user['id']);
        $_result = mysqli_query($_query, $_db_conn);
  	    if (!$_result) {
    		die('Invalid query: ' . mysql_error());
   		}
        $_jwt_result['result'] = 1;
    }
	break;
case 3:
    if (!is_null($channel_user) && $channel_user['is_expired']) {
    	$_jwt_result['content_expired'] = 1;
    }
    break;
}

// DB Close
mysqli_close($_db_conn);
// 결과인 data array를 JWT로 변환하여 출력함
print_kollus_jwt($_jwt_result);

샘플코드

 DRM – dotnet

 DRM – jsp

 DRM – php

  • No labels