如何使用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
CryptoKeyobjects, 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
- Helper Functions:
arrayBufferToBase64andbase64ToArrayBufferlet us convert binary data (like IVs and encrypted video) into strings that are easy to store in localStorage or send to a server. - Custom Key Handling:
getCustomCryptoKeyconverts your string key into aCryptoKeythat WebCrypto can use, and validates its length to ensure it’s compatible with AES. - 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.
- 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: falseinimportKeyto prevent the key from being exported (for better security). If you need to save the key elsewhere, set this totrueand export it usingwindow.crypto.subtle.exportKey. - Video Formats: Adjust the Blob type in
decryptVideoto match your video format (e.g.,video/webmfor WebM files).
内容的提问来源于stack exchange,提问作者asierta
相关产品推荐
相关产品推荐

