サポート問い合わせ先| システムステータス
ページコンテンツ

    の作成JSONWebトークン(JWT)

    このトピックでは、を作成する方法を学習しますJSONWebトークン(JWT)Brightcove PlaybackAPIと通信するときに使用できます。

    入門

    ビデオライブラリにアクセスするときに保護のレベルを追加したり、コンテンツにユーザーレベルの制限を適用したりするには、 JSONWebトークン(JWT)BrightcovePlayback APIへの呼び出し。トークンを作成するには、次の手順を実行します。

    1. 公開鍵と秘密鍵のペアを生成
    2. Brightcoveに公開鍵を登録する
    3. を作成しますJSONWebトークン
    4. テスト再生

    公開鍵と秘密鍵のペアを生成

    公開者は公開鍵と秘密鍵ペアを生成し、その公開鍵を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)

    • HLSE の場合、プレーヤーはビデオの再生時に複数の要求を行います。通常、レンディションごとに 1 つずつ。は、maxuこれらの追加要求に対応できる十分な高さに設定する必要があります。
    • DRM の場合、再生ごとに 1 つのライセンス要求が行われます。
    セッショントラッキングに必要です。
    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}
    

    ページの最終更新日22 Sep 2021