End-to-End Encryption in Web Apps via Web Crypto API
Kamal Tripathi
Founder & Lead Engineer, Share2Me
Building a genuinely secure web application requires end-to-end encryption (E2EE), ensuring that data is encrypted before it leaves the sender's device and remains encrypted until it is decrypted by the recipient. For years, implementing E2EE in a web browser meant bundling large third-party JavaScript libraries like CryptoJS or forge.js, which degraded performance, added attack surface, and required trusting a third-party code package. Modern browsers have solved this with the Web Crypto API — a fast, hardware-accelerated, browser-native cryptographic framework that is part of the W3C Web Cryptography specification. This article demonstrates in detail how to build a complete, production-grade E2EE pipeline using the Web Crypto API, covering ECDH key exchanges, AES-GCM-256 encryption, IV management, and Web Worker threading.
1. Why Browser-Native Cryptography Matters
Before the Web Crypto API was standardised (it became available in all major browsers around 2017), web developers depended on pure-JavaScript cryptography libraries. These libraries implement complex mathematical operations — elliptic curve arithmetic, AES block ciphers, SHA hashing — entirely in JavaScript, a language not designed for high-performance numerical computation. The result was that encrypting even a moderately sized file (say, 50 MB) would take tens of seconds, freezing the browser's user interface entirely. The Web Crypto API solves this by moving cryptographic operations out of JavaScript and into the browser engine itself, which is written in highly optimised C++ and can access dedicated hardware acceleration. On processors with Intel AES-NI or ARM Cryptographic Extensions, AES encryption runs at speeds exceeding 10 GB per second — fast enough to encrypt a 1 GB file in under 100 milliseconds. Security is equally important: the Web Crypto API enforces best practices by design. You cannot extract raw private key material from a CryptoKey object once it is marked as non-extractable. This prevents malicious scripts from stealing encryption keys even if they have access to your page's JavaScript context.
- Hardware Acceleration: AES-NI instructions allow AES-GCM to run at 10+ GB/s on modern CPUs, compared to ~50 MB/s in pure JavaScript.
- Non-Extractable Keys: CryptoKey objects can be flagged to prevent raw private key material from ever entering JavaScript memory.
- W3C Standard: The Web Crypto API is standardised, consistently implemented across Chrome, Firefox, Safari, and Edge.
- No Dependencies: Zero external library requirements means a smaller attack surface and no supply chain risk.
2. Key Exchange via Elliptic Curve Diffie-Hellman (ECDH)
For E2EE, both the sender and the receiver must independently derive the same shared encryption key without transmitting that key over the network. ECDH (Elliptic Curve Diffie-Hellman) is the standard algorithm for this. In Share2Me, the process works as follows: When a transfer is initiated, the sender's browser generates an ephemeral P-256 (NIST secp256r1) key pair — a private key and a public key — using window.crypto.subtle.generateKey. The receiver's browser does the same. The two browsers then exchange only their public keys via the signaling server's WebSocket connection. Each browser then calls window.crypto.subtle.deriveBits using its own private key and the peer's public key, producing the same 256-bit shared secret on both sides — without the secret ever leaving either device. This shared secret is then passed through HKDF (HMAC-based Key Derivation Function) to derive a proper 256-bit AES-GCM key. Because each transfer generates a completely fresh key pair, compromising one session's keys reveals nothing about other sessions — this property is called Perfect Forward Secrecy.
- NIST P-256 Curve: A widely trusted elliptic curve providing 128-bit symmetric security equivalence with fast computation.
- Ephemeral Keys: A fresh key pair is generated per transfer — leaked keys from one session cannot decrypt other sessions.
- Zero Key Transmission: The 32-byte shared secret is derived locally from public keys; it is never transmitted over the wire.
- HKDF Derivation: The raw ECDH output is passed through HKDF-SHA-256 to produce a properly formatted AES-GCM key.
3. AES-GCM-256 Symmetric Encryption with Per-Chunk IVs
Once the shared 256-bit AES key is established, the file is encrypted using AES-GCM (Galois/Counter Mode). GCM is far superior to the older AES-CBC (Cipher Block Chaining) mode for several reasons. First, GCM provides Authenticated Encryption with Associated Data (AEAD) — it produces both ciphertext and a 16-byte authentication tag. Before decryption, the receiver verifies this tag. If even a single bit of the encrypted data was altered in transit by a malicious intermediary, the tag will not match and decryption will throw an error, preventing tampered data from being accepted. Second, GCM mode is fully parallelisable — each cipher block can be encrypted or decrypted independently, unlike CBC which requires sequential processing. This makes GCM dramatically faster on modern multi-core processors. For file transfers, the file is split into fixed-size binary chunks (typically 128 KB each). Each chunk is encrypted with the same AES key but a different, randomly generated 12-byte Initialisation Vector (IV). The IV is not secret — it is prepended to the encrypted chunk in plaintext. Reusing the same IV with the same key is a critical vulnerability in GCM mode, so using a unique random IV per chunk is mandatory for security. Share2Me generates these IVs using window.crypto.getRandomValues, which pulls from the operating system's cryptographically secure random number generator.
- Authentication Tag: A 128-bit tag proves data integrity — any tampering is detected before decryption completes.
- Parallelisable: GCM's counter mode allows simultaneous encryption of multiple blocks, unlike sequential CBC chains.
- Per-Chunk IVs: Each 128 KB chunk gets a unique, randomly generated 12-byte IV to prevent IV-reuse vulnerabilities.
- AEAD Security: Authenticated Encryption provides both confidentiality (data is unreadable) and integrity (data is unmodified).
4. Web Workers: Keeping the UI Responsive
Even with hardware-accelerated AES-NI instructions, encrypting a 1 GB file involves processing millions of individual 128-byte blocks. If this computation runs on the browser's main JavaScript thread, the entire user interface freezes until completion. The solution is the Web Worker API, which allows JavaScript code to execute on a background thread, completely separate from the UI thread. In Share2Me, the encryption pipeline runs entirely inside a dedicated Web Worker: the file's ArrayBuffer is transferred (not copied) to the worker using a Transferable object, which takes zero time regardless of file size. The worker reads the file in chunks, calls window.crypto.subtle.encrypt for each chunk, and posts the encrypted binary chunk back to the main thread for transmission via the WebRTC DataChannel. The UI thread remains completely unblocked during this entire process, keeping the progress bar, cancel button, and all interface elements fully responsive.
Conclusion
By combining ephemeral ECDH key exchange with AES-GCM-256 AEAD encryption, per-chunk IV generation, and Web Workers for background processing, we build a complete E2EE pipeline that is both highly secure and performant. The result is a file transfer system where even the developer cannot read your data — because the encryption and decryption keys are generated locally in your browser and discarded when the session ends. Share2Me implements this complete cryptographic stack, making it one of the most privacy-preserving file transfer utilities available in a web browser today.
Written by Kamal Tripathi
Founder & Lead Engineer, Share2Me
Kamal is the founder of Share2Me and a full-stack engineer specialising in real-time browser communications, WebRTC, and cryptographic systems. He built Share2Me to solve the problem of cross-platform, privacy-first file sharing without requiring app installations.
Last updated: August 10, 2026