Introducción
Para agregar un nivel adicional de protección al acceder a su biblioteca de videos, o para aplicar restricciones de nivel de usuario para su contenido, puede pasar un Token web JSON (JWT) con su llamada a Brightcove Playback API. Para crear el token, siga estos pasos:
Generar par de claves público-privadas
El editor generará un par de claves pública-privada y proporcionará la clave pública a Brightcove. El editor utiliza la clave privada para firmar tokens y no se comparte con Brightcove.
Hay muchas formas de generar el par de claves pública-privada. Aquí hay unos ejemplos:
Ejemplo de script de bash:
Script de ejemplo para generar el par de claves:
#!/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"
Ejecute el script:
$ bash keygen.sh
Ejemplo usando Ir
Ejemplo usando el Ir lenguaje de programación para generar el par de claves:
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)
}
Ejemplo usando node.js
Ejemplo usando node.js para generar el par de claves:
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");
Registrar clave pública
Utilizará la API de claves para registrar su clave pública con Brightcove.
API clave
La API de claves se utiliza para administrar sus claves públicas con Brightcove.
URL base
La URL base de la API es:
https://playback-auth.api.brightcove.com
Ruta de la cuenta
En todos los casos, se realizarán solicitudes para una cuenta de Video Cloud específica. Por lo tanto, siempre agregará el término cuentas seguido de la identificación de su cuenta a la URL base:
https://playback-auth.api.brightcove.com/v1/accounts/{accountID}
Autorización
Se requiere un token de acceso para las solicitudes y debe estar presente en el encabezado de Autorización:
Authorization: Bearer {access_token}
El token de acceso es un token de acceso OAuth2 temporal que debe obtenerse del servicio Brightcove OAuth. Para obtener detalles sobre cómo obtener credenciales de cliente y usarlas para recuperar tokens de acceso, consulte la Descripción general de Brightcove OAuth.
Permisos
Las solicitudes a la API clave deben realizarse desde credenciales del cliente con los siguientes permisos:
-
video-cloud/playback-auth/key/read -
video-cloud/playback-auth/key/write
Administrar claves
La API clave admite las siguientes solicitudes:
Registre una nueva clave:
Pon el valor de tu clave pública en el cuerpo de la solicitud de API. Puede encontrar la clave en el public_key.txt expediente.
Pedido
POST /v1/accounts/{accountID}/keys
Content-Type: application/json
Body: {"value": "MFkwEwYHKoZIzj0CAQYIKoZIzj...MyeQviqploA=="}
Usando Curl
curl -X POST \\ -H "Tipo de contenido: aplicación / json" \\ -H "Autorización: Portador {access_token} "\ -d '{"value": "{your_public_key_value}«}' \ https://playback-auth.api.brightcove.com/v1/accounts/{accountID}/keys
Respuesta
{
"id": "{your_public_key_id}",
"type": "public",
"algorithm": "rsa",
"value": "{your_public_key_value}",
"createdAt": "2020-01-03T20:30:36.488Z"
}
Lista de claves:
Obtenga una lista de claves públicas en su cuenta.
GET /v1/accounts/{accountID}/keys
Obtenga una clave:
Obtenga los detalles de una clave pública en su cuenta.
GET /v1/accounts/{accountID}/keys/{key_Id}
Eliminar una clave:
Elimina una clave pública de tu cuenta.
DELETE /v1/accounts/{accountID}/keys/{key_Id}
Crear un Token web JSON
Los editores crean un Token web JSON (JWT). El token está firmado con el algoritmo RSA utilizando el algoritmo hash SHA-256 (identificado en la especificación JWT como "RS256") No se admitirán otros algoritmos JWT.
Un subconjunto del estándar Reclamaciones de JSON Web Token se utilizará, junto con algunas reclamaciones privadas definidas por Brightcove. Crearás un Token web JSON firmado con su clave privada.
Reclamaciones de entrega de URL estáticas
Para obtener una lista de afirmaciones que se pueden utilizar, consulte la Entrega de URL estática documento.
Reclamaciones de autorización de reproducción
Las siguientes afirmaciones se pueden utilizar con Servicio de autorización de reproducción de Brightcove.
| Campo | Tipo | Necesario | Descripción |
|---|---|---|---|
accid |
Cuerda | Sí | La identificación de la cuenta que posee el contenido que se está reproduciendo. |
exp |
Entero | Sí | Hora en que esta ficha ya no será válida, en segundos desde la Época. No debe ser más de 30 días desde iat |
iat |
Entero | Sí | Hora en que se emitió esta ficha, en segundos desde la Época |
conid |
Cuerda | Si está presente, este token solo autorizará la obtención de licencias para una identificación de video de Video Cloud específica.
Debe ser una identificación de video válida. |
|
maxip |
Entero | Si está presente, este token solo podrá ser utilizado por esta cantidad de direcciones IP diferentes. (DRM y AES-128)
Requerido para el seguimiento de la sesión. |
|
maxu |
Entero |
Si está presente, este token solo será válido para esta cantidad de solicitudes de licencia. (DRM y AES-128)
|
|
ua |
Cuerda | Si está presente, este token solo será válido para solicitudes de este agente de usuario.
Este campo no está validado. |
Reclamaciones de derechos de reproducción
Las siguientes afirmaciones se pueden utilizar con Servicio de gestión de derechos de reproducción de Brightcove.
| Campo | Tipo | Necesario | Obligatorio para los límites de transmisión simultánea | Solo DRM | Descripción |
|---|---|---|---|---|---|
accid |
Cuerda | Sí | La identificación de la cuenta que posee el contenido que se está reproduciendo. | ||
exp |
Entero | Sí | Hora en que esta ficha ya no será válida, en segundos desde la Época. No debe ser más de 30 días desde iat |
||
iat |
Entero | Sí | Hora en que se emitió esta ficha, en segundos desde la Época | ||
nbf |
Entero | Hora en que esta ficha comenzará a ser válida, en segundos desde la Época. | |||
pkid |
Cuerda | La identificación de clave pública utilizada para verificar este token. Está registrado en el Servicio de autorización de reproducción de Brightcove y debe utilizar el formato de clave RSA.
Si pkid se especifica, validamos el token con la clave especificada.
Si no pkid se especifica, recuperamos todas las claves de la cuenta e intentamos validarlas con todas.
|
|||
prid |
Cuerda | A playback_rights_id. Se usa para anular la identificación establecida en el catálogo de este video.
Este campo no está validado. |
|||
tags |
Matriz <Strings> | si está presente, este token es válido solo para las etiquetas enumeradas autorizadas a reproducir. | |||
vids |
Matriz <Strings> | Si está presente, este token solo autorizará la obtención de licencias para un conjunto de ID de video. | |||
cbeh |
Cuerda | Sí | Establezca el valor en BLOCK_NEW para habilitar los límites de transmisión simultánea para bloquear cualquier solicitud nueva, incluso del mismo usuario, cuando se alcanza el número máximo de transmisiones.
Establezca el valor en BLOCK_NEW_USER para bloquear cualquier solicitud nueva solo de un nuevo usuario cuando se alcanza el número máximo de transmisiones.
El valor predeterminado bloquea la transmisión más antigua cuando se alcanza el número máximo de transmisiones. Límites de transmisión simultánea: Opcional |
||
cexp |
Cuerda | Sí | Caducidad simultánea de la sesión: el valor predeterminado es 2 veces la duración del contenido o 15 minutos, lo que sea más largo.
Esto define cuánto tiempo es válida la sesión, después del cual el espectador final tiene que iniciar una nueva sesión para continuar la reproducción. Ejemplo: 2h o 42m
Límites de transmisión simultánea: Opcional |
||
climit |
Entero | Sí | Sí | Cuando se incluye este campo, habilita los límites de transmisión simultánea junto con las solicitudes de renovación de licencia. Este valor indica la cantidad de observadores simultáneos permitidos.
Límites de transmisión simultánea: Necesario |
|
dlimit |
Entero | Sí | Cuando se incluye este campo, controla cuántos dispositivos se pueden asociar con el usuario especificado (uid). El valor debe ser> 0.
Los dispositivos previamente permitidos seguirán funcionando si el dlimit el valor se elimina en solicitudes posteriores.
Ejemplo: si el valor se establece en 3, el usuario puede jugar en los dispositivos A, B y C (todo estaría permitido). Intentar jugar en el dispositivo D sería denegado.
Si el valor se cambia a 1 , el usuario aún puede jugar en los 3 dispositivos A, B y C, a menos que los dispositivos se revoquen manualmente mediante la administración de dispositivos con el API de dispositivos.
Registro de dispositivo: Necesario |
||
sid |
Cuerda | Sí | Especificar el ID de sesión de la transmisión actual le permite controlar cómo se define una sesión. De forma predeterminada, una sesión se define como User-Agent (navegador) + dirección IP + identificación de video.
Por ejemplo, puede aflojar la definición de sesión a dirección IP + identificación de video. Límites de transmisión simultánea: Opcional |
||
uid |
Cuerda | Sí | El ID de usuario del visor final. Este campo se utiliza para correlacionar varias sesiones para hacer cumplir los límites de transmisión simultánea.
Registro de dispositivo: Necesario |
Genera un token
Las bibliotecas están comúnmente disponibles para generar tokens JWT. Para obtener más detalles, consulte la Tokens web JSON sitio.
Ejemplo de script de bash:
Script de ejemplo para generar el token 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"
Ejecute el script:
$ bash jwtgen.sh
Ejemplo usando Ir
Aquí hay un ejemplo de una referencia. Ir implementación (como una herramienta cli) para generar tokens sin el uso de ninguna biblioteca de terceros:
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)
}
Resultados
Aquí hay un ejemplo de un token decodificado usando https://JWT.io especificando el conjunto completo de reclamaciones:
ENCABEZAMIENTO:
{
"alg": "RS256",
"type": "JWT"
}
CARGA ÚTIL:
{
"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"
}
Prueba de reproducción
Aunque no es obligatorio, es posible que desee probar la reproducción de video antes de configurar un reproductor.
Solicitar reproducción:
curl -X GET \
-H 'Authorization: Bearer {JWT}' \
https://edge-auth.api.brightcove.com/playback/v1/accounts/{your_account_id}/videos/{your_video_id}