You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用WebCrypto API结合AES与自定义密钥加解密视频

Using WebCrypto API with AES-CBC and Custom Keys (Encrypt + Decrypt)

Great question! Your existing code handles encryption with a randomly generated AES-CBC key, but as you noticed, it’s missing decryption support and doesn’t let you use a custom key. Let’s build a complete solution that fixes both gaps, with clear explanations for each step.

Key Background to Know

  • AES-CBC Rules: AES-CBC requires a 16-byte (128-bit) Initialization Vector (IV) — it needs to be random for every encryption but doesn’t need to be kept secret.
  • Custom Key Requirements: WebCrypto works with CryptoKey objects, so your custom key must be 16, 24, or 32 bytes long (for AES-128, AES-192, or AES-256 respectively). We’ll convert your key string into this format.
  • Persisting Data: To decrypt later, you need to save the IV, encrypted data, and your custom key (or a way to re-derive it). We’ll use Base64 encoding to make these binary values easy to store or share.

Full Implementation

First, let’s add simple HTML controls for file selection and encryption/decryption:

<input type="file" id="fileInput" accept="video/*">
<button id="encryptBtn">Encrypt Video</button>
<button id="decryptBtn" disabled>Decrypt Video</button>
<p id="status"></p>

Now the JavaScript code with complete encrypt/decrypt logic:

// Helper functions to convert between ArrayBuffer and Base64 (for easy storage)
function arrayBufferToBase64(buffer) {
  return btoa(String.fromCharCode(...new Uint8Array(buffer)));
}

function base64ToArrayBuffer(base64) {
  const binaryString = atob(base64);
  const bytes = new Uint8Array(binaryString.length);
  for (let i = 0; i < binaryString.length; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  return bytes.buffer;
}

// Replace this with your own custom key (16/24/32 chars for AES-128/192/256)
const CUSTOM_KEY = "my32ByteLongSecureKey123456789012";

// Convert custom key string to a WebCrypto-compatible CryptoKey
async function getCustomCryptoKey() {
  const encoder = new TextEncoder();
  const keyBytes = encoder.encode(CUSTOM_KEY);

  // Validate key length is valid for AES
  if (![16, 24, 32].includes(keyBytes.byteLength)) {
    throw new Error("Key must be 16, 24, or 32 characters long for AES-128/192/256");
  }

  return window.crypto.subtle.importKey(
    "raw",
    keyBytes,
    { name: "AES-CBC" },
    false, // Set to true only if you need to export the key later
    ["encrypt", "decrypt"]
  );
}

// Encrypt a selected video file
async function encryptVideo(file) {
  const status = document.getElementById('status');
  status.textContent = "Encrypting video...";

  try {
    // Read video file as ArrayBuffer
    const fileBuffer = await new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result);
      reader.onerror = reject;
      reader.readAsArrayBuffer(file);
    });

    // Get our custom CryptoKey
    const key = await getCustomCryptoKey();

    // Generate random 16-byte IV
    const iv = window.crypto.getRandomValues(new Uint8Array(16));

    // Encrypt the video data
    const encryptedBuffer = await window.crypto.subtle.encrypt(
      { name: "AES-CBC", iv: iv },
      key,
      fileBuffer
    );

    // Convert values to Base64 for storage
    const ivBase64 = arrayBufferToBase64(iv);
    const encryptedBase64 = arrayBufferToBase64(encryptedBuffer);

    // Save to localStorage (replace with server storage if needed)
    localStorage.setItem('encryptedVideo', encryptedBase64);
    localStorage.setItem('videoIV', ivBase64);

    status.textContent = "Encryption done! Ready to decrypt.";
    document.getElementById('decryptBtn').disabled = false;
  } catch (err) {
    status.textContent = `Encryption failed: ${err.message}`;
    console.error(err);
  }
}

// Decrypt the stored video and play it
async function decryptVideo() {
  const status = document.getElementById('status');
  status.textContent = "Decrypting video...";

  try {
    // Retrieve stored values
    const encryptedBase64 = localStorage.getItem('encryptedVideo');
    const ivBase64 = localStorage.getItem('videoIV');

    if (!encryptedBase64 || !ivBase64) {
      throw new Error("No encrypted video found — encrypt a file first!");
    }

    // Convert back to ArrayBuffers
    const encryptedBuffer = base64ToArrayBuffer(encryptedBase64);
    const iv = base64ToArrayBuffer(ivBase64);

    // Get our custom CryptoKey
    const key = await getCustomCryptoKey();

    // Decrypt the video data
    const decryptedBuffer = await window.crypto.subtle.decrypt(
      { name: "AES-CBC", iv: iv },
      key,
      encryptedBuffer
    );

    // Convert decrypted data to a playable video Blob
    const decryptedBlob = new Blob([decryptedBuffer], { type: "video/mp4" }); // Adjust type for your video format
    const videoUrl = URL.createObjectURL(decryptedBlob);

    // Create and add video element to the page
    const videoElement = document.createElement('video');
    videoElement.src = videoUrl;
    videoElement.controls = true;
    videoElement.style.maxWidth = "800px";
    document.body.appendChild(videoElement);

    status.textContent = "Decryption complete! Enjoy your video.";
  } catch (err) {
    status.textContent = `Decryption failed: ${err.message}`;
    console.error(err);
  }
}

// Attach event listeners to buttons
document.getElementById('encryptBtn').addEventListener('click', () => {
  const fileInput = document.getElementById('fileInput');
  if (!fileInput.files.length) {
    alert("Please select a video file first!");
    return;
  }
  encryptVideo(fileInput.files[0]);
});

document.getElementById('decryptBtn').addEventListener('click', decryptVideo);

Key Details Explained

  1. Helper Functions: arrayBufferToBase64 and base64ToArrayBuffer let us convert binary data (like IVs and encrypted video) into strings that are easy to store in localStorage or send to a server.
  2. Custom Key Handling: getCustomCryptoKey converts your string key into a CryptoKey that WebCrypto can use, and validates its length to ensure it’s compatible with AES.
  3. Encryption Flow: We read the video file, generate a random IV, encrypt the data with your custom key, then save the IV and encrypted data for later use.
  4. Decryption Flow: We retrieve the stored values, convert them back to binary, decrypt the data, and create a playable video Blob that you can watch directly in the browser.

Extra Tips

  • Password-Based Keys: If you want to use a user-provided password instead of a fixed key, use a key derivation function like PBKDF2 to generate a secure AES key from the password. Just ask if you need an example!
  • Key Security: We set extractable: false in importKey to prevent the key from being exported (for better security). If you need to save the key elsewhere, set this to true and export it using window.crypto.subtle.exportKey.
  • Video Formats: Adjust the Blob type in decryptVideo to match your video format (e.g., video/webm for WebM files).

内容的提问来源于stack exchange,提问作者asierta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:14:53