연락처 지원 | 시스템 상태
페이지 내용

    만들기 JSON 웹 토큰 (JWT)

    이 항목에서는 생성하는 방법을 배웁니다. JSON 웹 토큰 (JWT) Brightcove Playback API와 통신할 때 사용할 수 있습니다.

    소개

    비디오 라이브러리에 액세스할 때 추가 수준의 보호를 추가하거나 콘텐츠에 대한 사용자 수준 제한을 적용하려면 JSON 웹 토큰 (JWT) Brightcove Playback API를 호출합니다. 토큰을 생성하려면 다음 단계를 따르세요.

    1. 공개-개인 키 쌍 생성
    2. Brightcove에 공개 키 등록
    3. 만들기 JSON 웹 토큰
    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}

    액세스 토큰은 Brightcove OAuth 서비스에서 가져와야 하는 임시 OAuth2 액세스 토큰입니다. 클라이언트 자격 증명을 얻고 이를 사용하여 액세스 토큰을 검색하는 방법에 대한 자세한 내용은 다음을 참조하십시오. 브라이트코브 OAuth 개요 .

    권한

    Key API에 대한 요청은 다음에서 이루어져야 합니다. 클라이언트 자격 증명다음 권한으로:

    • video-cloud/playback-auth/key/read
    • video-cloud/playback-auth/key/write

    키 관리

    Key API는 다음 요청을 지원합니다.

    새 키 등록:

    API 요청 본문에 공개 키 값을 입력합니다. 에서 키를 찾을 수 있습니다. public_key.txt파일.

    요구
    POST /v1/accounts/{accountID}/keys
        Content-Type: application/json
        Body: {"value": "MFkwEwYHKoZIzj0CAQYIKoZIzj...MyeQviqploA=="}
    

    컬 사용하기

    curl -X POST \\ -H "콘텐츠 유형: 응용 프로그램/json" \\ -H "권한: 교군꾼{access_token} " \\ -NS ' {"value": "{your_public_key_value} "}'  \\ https://playback-auth.api.brightcove.com/v1/accounts/ {accountID} /키
    응답
    {
      "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

    하나의 키 가져오기:

    계정의 공개 키에 대한 세부 정보를 가져옵니다.

    GET /v1/accounts/{accountID}/keys/{key_Id}

    하나의 키 삭제:

    계정에서 공개 키를 삭제합니다.

    DELETE /v1/accounts/{accountID}/keys/{key_Id}

    만들기 JSON 웹 토큰

    게시자는 JSON 웹 토큰 (JWT). 토큰은 SHA-256 해시 알고리즘(JWT 사양에서 " RS256 ") 다른 JWT 알고리즘은 지원되지 않습니다.

    표준의 하위 집합 JSON 웹 토큰 클레임 Brightcove에서 정의한 일부 개인 청구와 함께 사용됩니다. 당신은 만들 것입니다 JSON 웹 토큰개인 키로 서명했습니다.

    정적 URL 전달에 대한 클레임

    사용할 수 있는 클레임 목록은 다음을 참조하세요. 정적 URL 전달문서.

    재생 승인 요청

    다음 주장은 다음과 함께 사용할 수 있습니다. Brightcove의 재생 인증 서비스 .

    유형 필수의 설명
    accid 재생 중인 콘텐츠를 소유한 계정 ID
    exp 정수 Epoch 이후 이 토큰이 더 이상 유효하지 않은 시간(초)입니다. 부터 30일 이내여야 합니다. iat
    iat 정수 이 토큰이 발행된 시간(Epoch 이후 초)
    conid 있는 경우 이 토큰은 특정 Video Cloud 비디오 ID에 대한 라이선스 가져오기만 승인합니다.

    유효한 동영상 ID여야 합니다.
    maxip 정수 존재하는 경우 이 토큰은 이렇게 다양한 IP 주소에서만 사용할 수 있습니다. (DRM 및 AES-128)

    세션 추적에 필요합니다.
    maxu 정수 있는 경우 이 토큰은 이 많은 라이선스 요청에 대해서만 유효합니다. (DRM 및 AES-128)

    • HLSe의 경우 플레이어는 비디오를 재생할 때 일반적으로 변환당 하나씩 여러 요청을 합니다. NS maxu이러한 추가 요청을 처리할 수 있을 만큼 충분히 높게 설정되어야 합니다.
    • DRM의 경우 재생당 하나의 라이선스 요청이 이루어집니다.
    세션 추적에 필요합니다.
    ua 존재하는 경우 이 토큰은 이 사용자 에이전트의 요청에 대해서만 유효합니다.

    이 필드는 검증되지 않았습니다.

    재생권 주장

    다음 주장은 다음과 함께 사용할 수 있습니다. Brightcove의 재생 권한 관리 서비스 .

    유형 필수의 동시 스트림 제한에 필요 DRM만 설명
    accid 재생 중인 콘텐츠를 소유한 계정 ID
    exp 정수 Epoch 이후 이 토큰이 더 이상 유효하지 않은 시간(초)입니다. 부터 30일 이내여야 합니다. iat
    iat 정수 이 토큰이 발행된 시간(Epoch 이후 초)
    nbf 정수 이 토큰이 유효하기 시작하는 시간(Epoch 이후 초 단위)
    pkid 이 토큰을 확인하는 데 사용되는 공개 키 ID입니다. Brightcove의 Playback Authorization Service에 등록되어 있으며 RSA 키 형식을 사용해야 합니다.

    만약에pkid지정되면 지정된 키로 토큰의 유효성을 검사합니다.

    그렇지 않은 경우pkid지정되면 계정에 대한 모든 키를 검색하고 모든 키에 대해 유효성을 검사합니다.
    prid NS 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를 지정하면 세션이 정의되는 방식을 제어할 수 있습니다. 기본적으로 세션은 User-Agent(브라우저) + IP 주소 + 비디오 ID로 정의됩니다.

    예를 들어 세션의 정의를 IP 주소 + 비디오 ID로 느슨하게 할 수 있습니다.

    동시 스트림 제한: 선택 과목
    uid 최종 뷰어의 사용자 ID입니다. 이 필드는 동시 스트림 제한을 적용하기 위해 여러 세션의 상관 관계를 지정하는 데 사용됩니다.

    장치 등록: 필수의

    토큰 생성

    라이브러리는 일반적으로 JWT 토큰을 생성하는 데 사용할 수 있습니다. 자세한 내용은 JSON 웹 토큰대지.

    예제 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 사용 예

    다음은 타사 라이브러리를 사용하지 않고 토큰을 생성하기 위한 참조 Go 구현(cli 도구)의 예입니다.

    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}
    

    페이지 마지막 업데이트 날짜16 Oct 2022