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 JSON Web Token (JWT) con su llamada a Brightcove Playback API.
Si eres nuevo en JWT's , revisa lo siguiente:
Flujo de trabajo
Para crear un JSON Web Token (JWT) y regístrese en Brightcove, siga estos pasos:
Generar par de claves público-privadas
Usted (el editor) generará un par de claves pública-privada y proporcionará la clave pública a Brightcove. Utilizará la clave privada para firmar tokens. La clave privada 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 Go
Ejemplo usando el Go 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
Eres propietario de la clave privada y la usarás para generar tokens firmados. Compartirá la clave pública con Brightcove para verificar sus tokens. La API de claves le permite registrar su clave pública con Brightcove.
Para obtener detalles de la API, consulte la Usar API de autenticación documento.
Crear un JSON Web Token
Los editores crean un JSON Web Token (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 JSON Web Token claims se utilizará, junto con algunas reclamaciones privadas definidas por Brightcove. Crearás un JSON Web Token firmado con su clave privada.
Reclamaciones de entrega de URL estáticas
Las siguientes afirmaciones se pueden utilizar con la entrega de URL estática de Brightcove .
Afirmar | Tipo | Requerido | Descripción |
---|---|---|---|
accid |
cuerda | La identificación de la cuenta que posee el contenido que se está reproduciendo. | |
iat |
Entero | Hora en que se emitió esta ficha, en segundos desde la Época | |
exp |
Entero |
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
|
|
drules |
Cuerda[] | Lista de ID de acción de regla de entrega para aplicar. Para obtener más información, consulte el documento Implementación de reglas de entrega . Si el config_id el parámetro de consulta también se establece, se ignorará, ya que este reclamo lo anula.
|
|
conid |
cuerda | Si está presente, este token solo autorizará una ID de video específica de Brightcove. Puede ser una transmisión DRM/HLSe o un activo que no sea DRM. Debe ser una identificación de video válida. Tenga en cuenta que no se admite el ID de referencia. |
|
pro |
cuerda | Especifica un tipo de protección en el caso de que haya varios disponibles para un solo video. Valores:
|
|
vod |
Objeto | Contiene opciones de configuración específicas para Video-On-Demand. | |
vod.ssai |
cuerda | Su ID de configuración de inserción de anuncios del lado del servidor (SSAI). Esta afirmación es requerido para recuperar un HLS o un DASH VMAP. |
A continuación se muestra un ejemplo de JSON Web Token (JWT) afirma que podría usar:
{
// account id: JWT is only valid for this accounts
"accid":"4590388311111",
// issued at: timestamp when the JWT was created
"iat":1575484132,
// expires: timestamp when JWT expires
"exp":1577989732,
// drules: list of delivery rule IDs to be applied
"drules": ["0758da1f-e913-4f30-a587-181db8b1e4eb"],
// content id: JWT is only valid for video ID
"conid":"5805807122222",
// protection: specify a protection type in the case where multiple are available for a single video
"pro":"aes128",
// VOD specific configuration options
"vod":{
// SSAI configuration to apply
"ssai":"efcc566-b44b-5a77-a0e2-d33333333333"
}
}
Reclamaciones por restricciones de reproducción
Las siguientes afirmaciones se pueden utilizar con Restricciones de reproducción de Brightcove. Como parte de las Restricciones de reproducción, puede implementar lo siguiente:
- Derechos de reproducción
- Protección de claves de licencia
- Restricciones de reproducción en vivo
- Flujos concurrentes
- Límites de dispositivos
- Reglas de entrega
Característica | Afirmar | Tipo | Requerido para la función | Solo DRM | Descripción |
---|---|---|---|---|---|
General | accid |
cuerda | Sí | La identificación de la cuenta que posee el contenido que se está reproduciendo. | |
iat |
Entero | Sí | Hora en que se emitió esta ficha, en segundos desde la Época | ||
exp |
Entero | Sí |
No es obligatorio, pero se recomienda encarecidamente. Tiempo en que este token ya no será válido, en segundos desde la Época. No debe ser más de 30 días desde iat
|
||
nbf |
Entero | Tiempo en que este token comenzará a ser válido, en segundos desde la Época. Si no se especifica, el token está disponible de inmediato. |
|||
Derechos de reproducción | prid |
cuerda | A playback_rights_id se usa para anular la identificación establecida en el catálogo para este video Este campo no está validado |
||
tags |
Matriz <Strings> | si está presente, este token solo es válido para videos que tienen los valores de etiquetas enumerados. Solo estos videos están autorizados para su reproducción. | |||
vids |
Matriz <Strings> | Si está presente, este token solo autorizará la obtención de licencias para un conjunto de ID de video. |
|||
Protección de claves de licencia | ua |
cuerda | Si está presente, este token solo será válido para solicitudes de este agente de usuario. Este campo no tiene que seguir ningún formato en particular. Debe tener habilitada la protección de claves de licencia . |
||
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 Debe tener habilitada la protección de claves de licencia . |
|||
maxip |
Entero | Sí | Si está presente, este token solo podrá ser utilizado por este número de direcciones IP diferentes. Requerido para el seguimiento de sesiones; HLSe (AES-128) solamente Debe tener habilitada la protección de claves de licencia . |
||
maxu |
Entero | Sí |
Si está presente, este token solo será válido para este número de solicitudes de licencia.
Debe tener habilitada la protección de claves de licencia . |
||
Flujos concurrentes | uid |
cuerda | Sí | Sí | El ID de usuario del visor final. Este campo se utiliza para correlacionar varias sesiones para hacer cumplir Stream Concurrency. Puede usar una identificación arbitraria (máx. 64 caracteres, limitado a AZ, az, 0-9 y =/,@_.+-). Pero, según su caso de uso, Brightcove recomienda un identificador de usuario para realizar un seguimiento de las sesiones por usuario o un identificador de cuenta para realizar un seguimiento de las sesiones por cuenta de pago. Requerido para la concurrencia de sesiones |
climit |
Entero | Sí | Sí | Cuando se incluye este campo, habilita la comprobación de la simultaneidad de la transmisión junto con las solicitudes de renovación de licencia. Este valor indica la cantidad de observadores simultáneos permitidos. Requerido para la concurrencia de sesiones |
|
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. |
||
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 + ID de video |
||
Límites de dispositivos | uid |
cuerda | Sí | Sí | El ID de usuario del visor final. Este campo se utiliza para correlacionar varias sesiones para hacer cumplir Stream Concurrency. Puede usar una identificación arbitraria (máx. 64 caracteres, limitado a AZ, az, 0-9 y =/,@_.+-). Pero, según su caso de uso, Brightcove recomienda un identificador de usuario para realizar un seguimiento de las sesiones por usuario o un identificador de cuenta para realizar un seguimiento de las sesiones por cuenta de pago. Requerido para el registro del dispositivo |
dlimit |
Entero | Sí | 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 (todos estarían permitidos). 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 derechos de reproducción. Requerido para el registro del dispositivo |
|
Reglas de entrega | drules |
Cuerda[] | Lista de ID de acción de regla de entrega para aplicar. Para obtener más información, consulte el documento Implementación de reglas de entrega .
|
Reclamaciones por nivel
Hay varios paquetes de seguridad disponibles para restricciones de reproducción. Para obtener más detalles, consulte la Descripción general: Documento de restricciones de reproducción de Brightcove.
Estas son las reclamaciones disponibles para cada paquete de restricción de reproducción:
Característica | Reclamación (es | Nivel de seguridad 1 | Nivel de seguridad 2 | Nivel de seguridad 3 |
---|---|---|---|---|
General | ácido | Sí | Sí | Sí |
Yo en | Sí | Sí | Sí | |
Exp | Sí | Sí | Sí | |
nbf | Sí | Sí | Sí | |
Derechos de reproducción [1] | orgullo | Sí | Sí | Sí |
etiquetas | Sí | Sí | Sí | |
videos | Sí | Sí | Sí | |
Protección de claves de licencia | tu | No | Sí | Sí |
conido | No | Sí | Sí | |
máximo | No | Sí | Sí | |
maximo | No | Sí | Sí | |
Flujos concurrentes | uid | No | No | Sí |
límite | No | No | Sí | |
cbeh | No | No | Sí | |
Sid | No | No | Sí | |
Transmisiones simultáneas genéricas | uid | No | No | Sí |
límite | No | No | Sí | |
Sid | No | No | Sí | |
Registro de dispositivo | uid | No | No | Sí |
delimitar | No | No | Sí |
Genera un token
Las bibliotecas están comúnmente disponibles para generar tokens JWT. Para obtener más detalles, consulte la JSON Web Tokens sitio.
una época & La herramienta de conversión de marca de tiempo de Unix puede ser útil cuando se trabaja con campos de tiempo.
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='{
"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 hour. Note: 3600 seconds = 1 hour
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 Go
A continuación se muestra un ejemplo de referencia. Go 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.
Entrega de URL estática
Solicitar reproducción:
curl -X GET \
https://edge.api.brightcove.com/playback/v1/accounts/{account_id}/videos/{video_id}/master.m3u8?bcov_auth={jwt}
Para obtener una lista de puntos finales de URL estáticas, consulte el documento Entrega de URL estáticas .
Restricciones de reproducción
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}