入門
ビデオライブラリにアクセスするときに保護のレベルを追加したり、コンテンツにユーザーレベルの制限を適用したりするには、 JSONWebトークン(JWT)BrightcovePlayback APIへの呼び出し。トークンを作成するには、次の手順を実行します。
公開鍵と秘密鍵のペアを生成
公開者は公開鍵と秘密鍵ペアを生成し、その公開鍵をBrightcoveに提供します。秘密鍵はパブリッシャーがトークンの署名に使用し、Brightcoveと共有されません。
公開鍵と秘密鍵のペアを生成する方法は多数あります。以下にいくつかの例を挙げます。
bashスクリプトの例:
キーペアを生成するスクリプト例:
#!/bin/bash
set -euo pipefail
NAME=${1:-}
test -z "${NAME:-}" && NAME="brightcove-playback-auth-key-$(date +%s)"
mkdir "$NAME"
PRIVATE_PEM="./$NAME/private.pem"
PUBLIC_PEM="./$NAME/public.pem"
PUBLIC_TXT="./$NAME/public_key.txt"
ssh-keygen -t rsa -b 2048 -m PEM -f "$PRIVATE_PEM" -q -N ""
openssl rsa -in "$PRIVATE_PEM" -pubout -outform PEM -out "$PUBLIC_PEM" 2>/dev/null
openssl rsa -in "$PRIVATE_PEM" -pubout -outform DER | base64 > "$PUBLIC_TXT"
rm "$PRIVATE_PEM".pub
echo "Public key to saved in $PUBLIC_TXT"
スクリプトを実行します。
$ bash keygen.sh
Go の使用例
Goプログラミング言語を使用してキーペアを生成する例:
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"flag"
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"time"
)
func main() {
var out string
flag.StringVar(&out, "output-dir", "", "Output directory to write files into")
flag.Parse()
if out == "" {
out = "rsa-key_" + strconv.FormatInt(time.Now().Unix(), 10)
}
if err := os.MkdirAll(out, os.ModePerm); err != nil {
panic(err.Error())
}
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err.Error())
}
privBytes := x509.MarshalPKCS1PrivateKey(priv)
pubBytes, err := x509.MarshalPKIXPublicKey(priv.Public())
if err != nil {
panic(err.Error())
}
privOut, err := os.OpenFile(path.Join(out, "private.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
panic(err.Error())
}
if err := pem.Encode(privOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: privBytes}); err != nil {
panic(err.Error())
}
pubOut, err := os.OpenFile(path.Join(out, "public.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
panic(err.Error())
}
if err := pem.Encode(pubOut, &pem.Block{Type: "PUBLIC KEY", Bytes: pubBytes}); err != nil {
panic(err.Error())
}
var pubEnc = base64.StdEncoding.EncodeToString(pubBytes)
var pubEncOut = path.Join(out, "public_key.txt")
if err := ioutil.WriteFile(pubEncOut, []byte(pubEnc+"\n"), 0600); err != nil {
panic(err.Error())
}
fmt.Println("Public key saved in " + pubEncOut)
}
node.js の使用例
node.js を使用してキーペアを生成する例:
var crypto = require("crypto");
var fs = require("fs");
var now = Math.floor(new Date() / 1000);
var dir = "rsa-key_" + now;
fs.mkdirSync(dir);
crypto.generateKeyPair(
"rsa",
{modulusLength: 2048},
(err, publicKey, privateKey) => {
fs.writeFile(
dir + "/public.pem",
publicKey.export({ type: "spki", format: "pem" }),
err => {}
);
fs.writeFile(
dir + "/public_key.txt",
publicKey.export({ type: "spki", format: "der" }).toString("base64") +
"\n",
err => {}
);
fs.writeFile(
dir + "/private.pem",
privateKey.export({ type: "pkcs1", format: "pem" }),
err => {}
);
}
);
console.log("Public key saved in " + dir + "/public_key.txt");
公開鍵を登録する
Key API を使用して、パブリックキーをBrightcoveに登録します。
キーAPI
Key API は、Brightcoveで公開キーを管理するために使用されます。
ベース URL
API のベース URL は次のとおりです。
https://playback-auth.api.brightcove.com
アカウントパス
いずれの場合も、特定の Video Cloud アカウントに対してリクエストが行われます。したがって、あなたは常にベースURLにアカウントIDが続く用語アカウントを追加します。
https://playback-auth.api.brightcove.com/v1/accounts/{accountID}
認証
リクエストのアクセストークンが必要であり、Authorizationヘッダに存在する必要があります።
Authorization: Bearer {access_token}
アクセストークンは、一時的な OAuth2 アクセストークンで、Brightcove OAuth サービスから取得する必要があります。クライアントクレデンシャルを取得してアクセストークンを取得する方法の詳細については、「 Brightcove OAuth の概要」を参照してください。
権限
Key API へのリクエストは、次のアクセス許可を持つクライアント認証情報から行う必要があります。
-
video-cloud/playback-auth/key/read -
video-cloud/playback-auth/key/write
キーを管理する
キー API は、次のリクエストをサポートします。
新しいキーを登録します。
API リクエスト本文に公開鍵の値を入力します。キーは public_key.txt ファイルにあります。
要求
POST /v1/accounts/{accountID}/keys
Content-Type: application/json
Body: {"value": "MFkwEwYHKoZIzj0CAQYIKoZIzj...MyeQviqploA=="}
カールの使用
curl -X POST \\ -H "Content-Type:application / json" \\ -H "認証:ベアラー{access_token} " \\ -NS ' {"value": "{your_public_key_value} "}' \\ https://playback-auth.api.brightcove.com/v1/accounts/ {accountID} / keys
応答
{
"id": "{your_public_key_id}",
"type": "public",
"algorithm": "rsa",
"value": "{your_public_key_value}",
"createdAt": "2020-01-03T20:30:36.488Z"
}
キーをリストする:
アカウント内の公開鍵のリストを取得します。
GET /v1/accounts/{accountID}/keys
キーを 1 つ取得する:
アカウントの公開鍵の詳細を取得します。
GET /v1/accounts/{accountID}/keys/{key_Id}
キーを 1 つ削除する:
アカウントの公開鍵を削除します。
DELETE /v1/accounts/{accountID}/keys/{key_Id}
を作成しますJSONWebトークン
出版社はJSONWebトークン(JWT)。トークンは、SHA-256 ハッシュアルゴリズムを使用して RSA アルゴリズムで署名されます (JWT 仕様では「 RS256 」として識別されます)。他の JWT アルゴリズムはサポートされません。
標準のサブセットJSONWebトークンの主張 Brightcoveによって定義されたいくつかの私的な主張とともに使用されます。を作成しますJSONWebトークン秘密鍵で署名しました。
静的 URL 配信のクレーム
使用できるクレームのリストについては、静的URL配信資料。
再生承認の申し立て
Brightcoveの再生認証サービスでは、次のクレームを使用できます。
| フィールド | タイプ | 必須 | 説明 |
|---|---|---|---|
accid |
文字列 | はい | 再生中のコンテンツを所有するアカウント ID |
exp |
整数 | はい | このトークンが有効でなくなる時間(エポックからの秒単位)。30日以内でなければなりませんiat |
iat |
整数 | はい | このトークンが発行された時間(エポックからの秒単位) |
conid |
文字列 | 存在する場合、このトークンは特定の Video Cloud ビデオ ID のライセンス取得のみを許可します。
有効な動画IDである必要があります。 |
|
maxip |
整数 | 存在する場合、このトークンは、この多くの異なる IP アドレスでのみ使用できます。(DRM&AES-128)
セッショントラッキングに必要です。 |
|
maxu |
整数 |
存在する場合、このトークンは、この数のライセンス要求に対してのみ有効です。(DRM&AES-128)
|
|
ua |
文字列 | 存在する場合、このトークンは、このユーザーエージェントからの要求に対してのみ有効です。
このフィールドは検証されません。 |
再生権の申し立て
Brightcoveの再生著作権管理サービスでは、次のクレームを使用できます。
| フィールド | タイプ | 必須 | 同時ストリーム制限に必要 | DRMのみ | 説明 |
|---|---|---|---|---|---|
accid |
文字列 | はい | 再生中のコンテンツを所有するアカウント ID | ||
exp |
整数 | はい | このトークンが有効でなくなる時間(エポックからの秒単位)。30日以内でなければなりませんiat |
||
iat |
整数 | はい | このトークンが発行された時間(エポックからの秒単位) | ||
nbf |
整数 | このトークンが有効になる時間 (エポックからの秒単位) | |||
pkid |
文字列 | このトークンの検証に使用される公開キー ID。BrightcoveのPlaybackAuthorization Serviceに登録されており、RSAキー形式を使用する必要があります。
もしも pkidが指定されている場合、指定されたキーでトークンを検証します。
いいえの場合 pkidが指定されている場合、アカウントのすべてのキーを取得し、それらすべてに対して検証を試みます。
|
|||
prid |
文字列 | A playback_rights_id .このビデオのカタログに設定されているIDを上書きするために使用されます
このフィールドは検証されません。 |
|||
tags |
<配列文字列 > | 存在する場合、このトークンは、再生が許可されたリストされたタグに対してのみ有効です。 | |||
vids |
<配列文字列 > | 存在する場合、このトークンは、一連のビデオ ID のライセンス取得のみを許可します。 | |||
cbeh |
文字列 | はい | 値をに設定しますBLOCK_NEWストリームの最大数に達したときに、同じユーザーからであっても、同時ストリーム制限を有効にして新しい要求をブロックします。
値をに設定します BLOCK_NEW_USERストリームの最大数に達したときにのみ、新しいユーザーからの新しい要求をブロックします。
デフォルトでは、ストリームの最大数に達すると、最も古いストリームがブロックされます。 同時ストリーム制限:オプション |
||
cexp |
文字列 | はい | セッションの同時実行期限-デフォルトは、コンテンツ期間の2倍または15分のいずれか長い方です。
これは、セッションが有効である期間を定義します。その後、エンドビューアは、再生を続行するために新しいセッションを開始する必要があります。例: 2hまた42m
同時ストリーム制限:オプション |
||
climit |
整数 | はい | はい | このフィールドを含めると、ライセンス更新要求とともに同時ストリーム制限が有効になります。この値は、許可される同時ウォッチャーの数を示します。
同時ストリーム制限:必須 |
|
dlimit |
整数 | はい | このフィールドを含めると、指定されたユーザに関連付けることができるデバイスの数を制御します(uid)。値はでなければなりません > 0。
以前に許可されたデバイスは、 dlimit値は後のリクエストで削除されます。
例:値がに設定されている場合 3、ユーザーはデバイスA、B、およびCでプレイできます(すべて許可されます)。デバイス D で再生しようとすると拒否されます。
値がに変更された場合 1、ユーザーは、デバイスを管理することによってデバイスが手動で取り消されない限り、3つのデバイスA、B、およびCすべてで引き続きプレイできます。デバイスAPI。
デバイス登録:必須 |
||
sid |
文字列 | はい | 現在のストリームのセッションIDを指定すると、セッションの定義方法を制御できます。デフォルトでは、セッションはユーザーエージェント(ブラウザ)+ IPアドレス+ビデオIDとして定義されます。
たとえば、セッションの定義をIPアドレス+ビデオIDに緩めることができます。 同時ストリーム制限:オプション |
||
uid |
文字列 | はい | エンドビューアのユーザー ID。このフィールドは、複数のセッションを相互に関連付けて、同時ストリーム制限を適用するために使用されます。
デバイス登録:必須 |
トークンを生成する
JWTトークンを生成するためのライブラリは一般的に利用可能です。詳細については、 JSONWebトークン地点。
bashスクリプトの例:
JWT トークンを生成するスクリプト例:
#! /usr/bin/env bash
# Static header fields.
HEADER='{
"type": "JWT",
"alg": "RS256"
}'
payload='{
"pkid": "{your_public_key_id}",
"accid": "{your_account_id}"
}'
# Use jq to set the dynamic `iat` and `exp`
# fields on the payload using the current time.
# `iat` is set to now, and `exp` is now + 1 second.
PAYLOAD=$(
echo "${payload}" | jq --arg time_str "$(date +%s)" \
'
($time_str | tonumber) as $time_num
| .iat=$time_num
| .exp=($time_num + 60 * 60)
'
)
function b64enc() { openssl enc -base64 -A | tr '+/' '-_' | tr -d '='; }
function rs_sign() { openssl dgst -binary -sha256 -sign playback-auth-keys/private.pem ; }
JWT_HDR_B64="$(echo -n "$HEADER" | b64enc)"
JWT_PAY_B64="$(echo -n "$PAYLOAD" | b64enc)"
UNSIGNED_JWT="$JWT_HDR_B64.$JWT_PAY_B64"
SIGNATURE=$(echo -n "$UNSIGNED_JWT" | rs_sign | b64enc)
echo "$UNSIGNED_JWT.$SIGNATURE"
スクリプトを実行します。
$ bash jwtgen.sh
Go の使用例
サードパーティのライブラリを使用せずにトークンを生成するための(CLIツールとしての)参照Go実装の例を次に示します。
package main
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"flag"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
)
// Header is the base64UrlEncoded string of a JWT header for the RS256 algorithm
const RSAHeader = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"
// Header is the base64UrlEncoded string of a JWT header for the EC256 algorithm
const ECHeader = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"
// Claims represents constraints that should be applied to the use of the token
type Claims struct {
Iat float64 `json:"iat,omitempty"` // Issued At
Exp float64 `json:"exp,omitempty"` // Expires At
Accid string `json:"accid,omitempty"` // Account ID
Conid string `json:"conid,omitempty"` // Content ID
Maxu float64 `json:"maxu,omitempty"` // Max Uses
Maxip float64 `json:"maxip,omitempty"` // Max IPs
Ua string `json:"ua,omitempty"` // User Agent
}
func main() {
var key, algorithm string
c := Claims{Iat: float64(time.Now().Unix())}
flag.StringVar(&key, "key", "", "Path to private.pem key file")
flag.StringVar(&c.Accid, "account-id", "", "Account ID")
flag.StringVar(&c.Conid, "content-id", "", "Content ID (eg, video_id or live_job_id)")
flag.Float64Var(&c.Exp, "expires-at", float64(time.Now().AddDate(0, 0, 1).Unix()), "Epoch timestamp (in seconds) for when the token should stop working")
flag.Float64Var(&c.Maxu, "max-uses", 0, "Maximum number of times the token is valid for")
flag.Float64Var(&c.Maxip, "max-ips", 0, "Maximum number of unique IP addresses the token is valid for")
flag.StringVar(&c.Ua, "user-agent", "", "User Agent that the token is valid for")
flag.StringVar(&algorithm, "algo", "", "Key algorithm to use for signing. Valid: ec256, rsa256")
flag.Parse()
if key == "" {
fmt.Printf("missing required flag: -key\n\n")
flag.Usage()
os.Exit(1)
}
if algorithm == "" {
fmt.Printf("missing required flag: -algo\n\n")
flag.Usage()
os.Exit(2)
}
if algorithm != "rsa256" && algorithm != "ec256" {
fmt.Printf("missing valid value for -algo flag. Valid: rsa256, ec256\n\n")
flag.Usage()
os.Exit(3)
}
if c.Accid == "" {
fmt.Printf("missing required flag: -account-id\n\n")
flag.Usage()
os.Exit(4)
}
bs, err := json.Marshal(c)
if err != nil {
fmt.Println("failed to marshal token to json", err)
os.Exit(5)
}
kbs, err := ioutil.ReadFile(key)
if err != nil {
fmt.Println("failed to read private key", err)
os.Exit(6)
}
if algorithm == "rsa256" {
processRSA256(kbs, bs)
} else {
processEC256(kbs, bs)
}
}
func processRSA256(kbs, bs []byte) {
block, _ := pem.Decode(kbs)
if block == nil {
fmt.Println("failed to decode PEM block containing private key")
os.Exit(7)
}
if block.Type != "RSA PRIVATE KEY" {
fmt.Println("failed to decode PEM block containing private key")
os.Exit(8)
}
pKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
fmt.Println("failed to parse rsa private key", err)
os.Exit(9)
}
message := RSAHeader + "." + base64.RawURLEncoding.EncodeToString(bs)
hash := crypto.SHA256
hasher := hash.New()
_, _ = hasher.Write([]byte(message))
hashed := hasher.Sum(nil)
r, err := rsa.SignPKCS1v15(rand.Reader, pKey, hash, hashed)
if err != nil {
fmt.Println("failed to sign token", err)
os.Exit(10)
}
sig := strings.TrimRight(base64.RawURLEncoding.EncodeToString(r), "=")
fmt.Println(message + "." + sig)
}
func processEC256(kbs, bs []byte) {
block, _ := pem.Decode(kbs)
if block == nil {
fmt.Println("failed to decode PEM block containing private key")
os.Exit(7)
}
if block.Type != "EC PRIVATE KEY" {
fmt.Println("failed to decode PEM block containing private key")
os.Exit(8)
}
pkey, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
fmt.Println("failed to parse ec private key", err)
os.Exit(9)
}
message := ECHeader + "." + base64.RawURLEncoding.EncodeToString(bs)
hash := sha256.Sum256([]byte(message))
r, s, err := ecdsa.Sign(rand.Reader, pkey, hash[:])
if err != nil {
fmt.Println("failed to sign token", err)
os.Exit(10)
}
curveBits := pkey.Curve.Params().BitSize
keyBytes := curveBits / 8
if curveBits%8 > 0 {
keyBytes++
}
rBytes := r.Bytes()
rBytesPadded := make([]byte, keyBytes)
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
sBytes := s.Bytes()
sBytesPadded := make([]byte, keyBytes)
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
out := append(rBytesPadded, sBytesPadded...)
sig := base64.RawURLEncoding.EncodeToString(out)
fmt.Println(message + "." + sig)
}
結果
以下に、クレームの全セットを指定する https://JWT.io を使用してデコードされたトークンの例を示します。
ヘッダー:
{
"alg": "RS256",
"type": "JWT"
}
ペイロード:
{
"accid": "1100863500123",
"conid": "51141412620123",
"exp": 1554200832,
"iat": 1554199032,
"maxip": 10,
"maxu": 10,
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
}
テスト再生
必須ではありませんが、プレーヤーを設定する前にビデオ再生をテストすることをお勧めします。
再生をリクエスト:
curl -X GET \
-H 'Authorization: Bearer {JWT}' \
https://edge-auth.api.brightcove.com/playback/v1/accounts/{your_account_id}/videos/{your_video_id}