# ขั้นตอนการสร้าง Authorization Token สำหรับใช้งาน DRM License Server
เพื่อให้ player สามารถเล่น content ที่ได้รับการป้องกันด้วย ByteArk Multi-DRM player จะต้องทำการขอ license ไปที่ license server ของทาง ByteArk ซึ่งจะทำการ authorize โดยการตรวจสอบ Authorization header ของ request นั้น ว่ามี JWT Token ที่ถูกต้องหรือไม่ โดยต้องมี 2 tokens แยกกันสำหรับ Widevine และ FairPlay
# ลำดับการทำงานในการใช้งาน Authorization Token
# ขั้นตอนการสร้าง Token เพื่อนำไปใช้กับ player มีดังนี้
- ให้ทำการสร้าง public-private key pair ซึ่งสามารถใช้ algorithm
ECDSA
,RSA
หรือEdDSA
เท่านั้น - เก็บ private key ไว้เป็นความลับ แล้วนำ public key มา upload ผ่านระบบของ ByteArk Stream หรือในกรณีเป็น live streaming สามารถติดต่อผู้ประสานงานเพื่อประสานช่องทางการส่ง key
- หลังจากส่ง public key จะได้รับข้อมูลที่จำเป็นในการสร้าง token เพิ่มเติมคือ
- key_id สำหรับ Widevine ซึ่งจะเป็น integer
- key_id สำหรับ FairPlay ซึ่งจะเป็น string
- สร้าง JWT Token สองอันเพื่อใช้กับ Widevine และ FairPlay, sign ด้วย algorithm
ES256
,RS256
หรือEdDSA
ซึ่งจะต้องสัมพันธ์กับ keypair ที่สร้างในข้อ 1 และจะใช้ private key ข้างต้นในการ sign Token จะต้องมีส่วนประกอบที่บังคับ (ใส่อื่นๆได้แต่ห้ามขาด) คือ- Header
kid
ซึ่งจะต้องมีค่าตรงกับ key_id ของ Widevine หรือ FairPlay ที่ได้มาในข้อ 3 - Payload
exp
ซึ่งกำหนดอายุการใช้งานของ token นี้ สามารถกำหนดได้ตามต้องการ, เป็น Unix timestamp
- Header
# ตัวอย่างการสร้าง JWT Token ผ่านหน้าเว็บ https://jwt.io/ (opens new window)
# ตัวอย่างการสร้าง JWT ด้วย Code
สามารถนำตัวอย่าง method generateJWT
ไปใช้งานกับ framework ที่ท่านใช้อยู่ได้
# ตัวอย่าง Code Golang สำหรับสร้าง JWT Token
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"time"
"github.com/golang-jwt/jwt/v5"
)
func generateJWT(privateKeyPEM []byte, kid interface{}) (string, error) {
key, err := jwt.ParseECPrivateKeyFromPEM(privateKeyPEM)
if err != nil {
return "", fmt.Errorf("error parsing private key: %w", err)
}
claims := jwt.MapClaims{
"sub": "license_auth",
"exp": time.Now().Add(24 * time.Hour).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
token.Header["kid"] = kid
signedToken, err := token.SignedString(key)
if err != nil {
return "", fmt.Errorf("error signing token: %w", err)
}
return signedToken, nil
}
func main() {
privateKeyPEM, err := os.ReadFile("private_key.pem")
if err != nil {
log.Fatalf("Error reading private key: %v", err)
}
widevineToken, err := generateJWT(privateKeyPEM, 12345678)
if err != nil {
log.Fatalf("Widevine token error: %v", err)
}
fairplayToken, err := generateJWT(privateKeyPEM, "testtestwidevine")
if err != nil {
log.Fatalf("FairPlay token error: %v", err)
}
output := map[string]string{
"widevine": widevineToken,
"fairplay": fairplayToken,
}
jsonOutput, err := json.MarshalIndent(output, "", " ")
if err != nil {
log.Fatalf("Error formatting JSON: %v", err)
}
fmt.Println(string(jsonOutput))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# ตัวอย่าง Code Node.js สำหรับสร้าง JWT Token
npm install --save jsonwebtoken
1
const jwt = require('jsonwebtoken');
const fs = require('fs');
// Load private key
const privateKey = fs.readFileSync('private_key.pem', 'utf8');
function generateJWT(privateKey, kid) {
const claims = {
sub: 'license_auth',
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24
};
return jwt.sign(claims, privateKey, {
algorithm: 'ES256',
header: { kid }
});
}
try {
const widevineToken = generateJWT(privateKey, 12345678);
const fairplayToken = generateJWT(privateKey, 'testtestwidevine');
const output = {
widevine: widevineToken,
fairplay: fairplayToken
};
console.log(JSON.stringify(output, null, 2));
} catch (err) {
console.error('Error generating token:', err);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# ตัวอย่าง Code PHP สำหรับสร้าง JWT Token
composer require firebase/php-jwt
1
<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
/**
* @param string $privateKey
* @param string|int $kid
* @return string
*/
function generateJWT(string $privateKey, string|int $kid): string {
$payload = [
'sub' => 'license_auth',
'exp' => time() + 60 * 60 * 24
];
$headers = ['kid' => $kid];
return JWT::encode($payload, $privateKey, 'ES256', null, $headers);
}
$privateKey = file_get_contents('private_key.pem');
$tokens = [
'widevine' => generateJWT($privateKey, 12345678),
'fairplay' => generateJWT($privateKey, 'testtestwidevine'),
];
header('Content-Type: application/json');
echo json_encode($tokens, JSON_PRETTY_PRINT);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# การสร้าง Public-Private Keypair
สามารถสร้างได้หลากหลายวิธี จะแนะนำวิธีการสร้างด้วย command line tool openssl
# สร้างผ่าน OpenSSL command line tools
# ตัวอย่างกาสร้าง RSA public-private keypair
# Generate private key
openssl genpkey -algorithm RSA -out rsa_private.pem -pkeyopt rsa_keygen_bits:2048
# Extract public key
openssl rsa -pubout -in rsa_private.pem -out rsa_public.pem
1
2
3
4
5
2
3
4
5
# ตัวอย่างกาสร้าง ECDSA public-private keypair
# Generate private key
openssl ecparam -name secp256r1 -genkey -noout -out ecdsa_private.pem
# Extract public key
openssl ec -in ecdsa_private.pem -pubout -out ecdsa_public.pem
1
2
3
4
5
2
3
4
5
# ตัวอย่างกาสร้าง EdDSA public-private keypair
# Generate private key
openssl genpkey -algorithm Ed25519 -out ed25519_private.pem
# Extract public key
openssl pkey -in ed25519_private.pem -pubout -out ed25519_public.pem
1
2
3
4
5
2
3
4
5