Hello,
If you’re delivering premium video content, Digital Rights Management (DRM) is crucial to prevent unauthorized access, piracy, and redistribution. While industry-standard DRM solutions like Google Widevine, Apple FairPlay, and Microsoft PlayReady offer robust protection, they come with licensing fees, platform restrictions, and vendor dependencies.
But what if you don’t want to rely on Widevine, PlayReady, or FairPlay?
In this post, I’ll guide you through developing your own DRM system using Bento4 and Node.js, allowing you to encrypt, distribute, and control access to your videos without third-party DRM providers.
🔍 Why Build Your Own DRM System?
✅ No Licensing Fees – Avoid expensive DRM licensing costs.
✅ Full Control – Define your own encryption policies and access rules.
✅ Cross-Platform – Work on any device without dependency on proprietary DRM.
✅ Customization – Implement domain restrictions, time-limited access, or watermarking.
❌ Challenges of a Custom DRM System
🚫 Less secure than Widevine, FairPlay, or PlayReady.
🚫 May not work on certain platforms that enforce standard DRM.
A custom DRM system is best suited for private video platforms, online courses, corporate training videos, or internal media distribution.
🛠 Building a Custom DRM System Using Bento4 & Node.js
Overview of the Process
Encrypt the Video → Protect your content using AES-128 or AES-256 encryption.
Generate Encryption Keys → Securely store and manage keys.
Deploy a License Server → Handle decryption key distribution.
Build a Secure Video Player → Authenticate users before playback.
Step 1: Install Bento4 & Convert Videos to MP4
Ensure your video is in MP4 format before encryption.
Install Bento4:
wget -O Bento4-SDK.tar.gz https://www.bento4.com/downloads/Bento4-SDK-xxxx-linux.zip
tar -xvf Bento4-SDK.tar.gz
cd Bento4-SDK
Check video format:
mp4info input.mp4
If the video is not in MP4 format, use FFmpeg:
ffmpeg -i input.mov -c:v libx264 -c:a aac output.mp4
Step 2: Encrypt the Video Using AES-128
We will encrypt the video using AES-128, making it unreadable without the decryption key.
Generate an AES Key
openssl rand 16 > aes.key
openssl rand 16 > aes.iv
Encrypt the MP4 File
mp4encrypt --method MPEG-CENC \
--key 1:<16-byte key>:<16-byte IV> \
--property 1:KID:<16-byte key ID> \
input.mp4 output_encrypted.mp4
Example:
mp4encrypt --method MPEG-CENC \
--key 1:1234567890abcdef1234567890abcdef:abcdef1234567890abcdef1234567890 \
--property 1:KID:abcdefabcdefabcdefabcdefabcdefab \
input.mp4 output_encrypted.mp4
The encrypted video is now useless without the decryption key.
Step 3: Store and Manage Encryption Keys Securely
Instead of hardcoding keys, store them in a secure backend database.
Example: Storing the Key in a Node.js Server Using MongoDB
const mongoose = require('mongoose');
const KeySchema = new mongoose.Schema({
videoId: String,
encryptionKey: String,
iv: String,
expiresAt: Date
});
module.exports = mongoose.model('Key', KeySchema);
Step 4: Build a License Server Using Node.js
A License Server validates users and delivers the decryption key.
Install Dependencies
npm init -y
npm install express mongoose dotenv
Create a License Server (server.js)
require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const Key = require('./models/Key');
mongoose.connect(process.env.MONGO_URI);
const app = express();
app.get('/license/:videoId', async (req, res) => {
const { videoId } = req.params;
// Validate user authentication
if (!req.headers.authorization) {
return res.status(403).json({ error: "Unauthorized" });
}
// Retrieve key from DB
const key = await Key.findOne({ videoId });
if (!key) {
return res.status(404).json({ error: "Key not found" });
}
res.json({ encryptionKey: key.encryptionKey, iv: key.iv });
});
app.listen(3000, () => console.log('License Server Running'));
Start the License Server
node server.js
Your Node.js DRM License Server is now running!
Step 5: Securely Deliver Encrypted Videos
Upload output_encrypted.mp4 to AWS S3, Cloudflare, or a CDN.
Restrict access using Signed URLs or JWT Authentication.
Example: Generating a Signed URL for Secure Access
const crypto = require('crypto');
function generateSignedUrl(videoUrl, secretKey) {
const expiry = Math.floor(Date.now() / 1000) + 3600; // Expires in 1 hour
const hash = crypto.createHmac('sha256', secretKey)
.update(videoUrl + expiry)
.digest('hex');
return `${videoUrl}?expires=${expiry}&signature=${hash}`;
}
Step 6: Build a Secure Video Player
Use Video.js with AES key fetching from your License Server.
<video id="video" controls></video>
<script >
async function loadVideo() {
const response = await fetch('https://yourserver.com/license/video123', {
headers: { "Authorization": "Bearer user-auth-token" }
});
const { encryptionKey, iv } = await response.json();
const player = videojs('video', {
sources: [{
src: "https://yourcdn.com/encrypted_video.mp4",
type: "video/mp4",
keySystems: {
"org.w3.clearkey": { "keys": { "kid": encryptionKey } }
}
}]
});
player.play();
}
document.addEventListener('DOMContentLoaded', loadVideo);
🛡️ Additional Security Enhancements
Rotate Encryption Keys Periodically
Issue new keys every few hours or days.
Watermarking
Embed a unique user ID as a watermark in the video.
Restrict Playback by Domain/IP
Prevent sharing by allowing only authorized domains.
Expire License Keys
Implement temporary keys that expire after playback.
🎯 Final Thoughts
Building your own DRM system using Bento4 & Node.js gives you control and flexibility while keeping costs low. However, it’s not as secure as Widevine, FairPlay, or PlayReady, so it’s best for private content, internal training videos, or niche streaming platforms.
If you found this guide helpful, let me know in the comments! Your support inspires me to create more actionable DRM and streaming guides 🚀.