Product Updates | Contact Support | System Status
Page Contents

    Forensic Watermarking with Beacon Apps

    In this topic, you will learn how to implement Forensic Watermarking with Beacon apps.

    Introduction

    Brightcove has partnered with NAGRA to provide Forensic Watermarking with Beacon apps to help protect premium content from piracy and unauthorized content sharing. With this feature, customers will be able to identify the source of a content leak and take appropriate action.

    The following diagram shows an overview:

    • Content preparation
      • The Forensic Watermark is an invisible watermark embedded into the video during transcoding using Nagra’s SDK
      • Ingestion creates 2 VOD renditions, one with watermark A and another with watermark B; both renditions are part of the same title in Video Cloud
    • Delivery
      • When playing the content, the Forensic Watermark token is provided to the player, then included in the URL used to request content from the CDN
      • The CDN interprets the token and delivers the video with the correct sequence of A/B segments to the viewer
    Overview diagram
    Forensic watermarking overview

    Requirements

    The following requirements are needed to support this feature:

    • Beacon apps need to use OIDC authentication
    • Only available for VOD assets
    • MP4 renditions will not be generated for watermarked videos

    Setup

    The following setup is needed to support Brightcove's Forensic Watermarking solution:

    1. Customer Video Cloud account:
      • Make sure the customer's account is enabled for Dynamic Delivery.
      • Open a customer support ticket to enable the customer's account for Forensic Watermarking; This is a paid add-on to Video Cloud.
    2. The customer will get their License Key from NAGRA.
    3. The customer will generate a public-private key pair which will be used by the Forensic Watermarking Token (WMT) and decrypted by the CDN. For examples, see the section below.
    4. The customer will use the script provided by NAGRA to generate a Forensic Watermarking Token (WMT).
    5. From the customer, request the name of the claim that will pass the Watermarking Token (WMT) to the apps. Share this claim with the Beacon team, so that they can configure it in the Beacon Master CMS.

    Generate a public-private key pair

    There are many ways to generate the public-private key pair. Here are some examples:

    Example bash script:

    Example script to generate the key pair:

    #!/bin/bash
    set -euo pipefail
    
    NAME=${1:-}
    test -z "${NAME:-}" && NAME="brightcove-forensic-watermarking-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"
    

    Run the script:

    $ bash keygen.sh
    
    Example using Go

    Example using the Go programming language to generate the key pair:

    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)
      }
      

    Example using node.js

    Example using node.js to generate the key pair:

    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");

    Supported features and limitations

    For a list of supported features and limitations when using Forensic Watermarking, see the Overview: Forensic Watermarking document.


    Page last updated on 03 Feb 2022