FileDummy Logo
FileDummy
File Security & Integrity

How to Encrypt a File Before Uploading: AES-256 in the Browser

Encrypt files client-side with AES-256-GCM using the Web Crypto API before uploading to cloud storage. Zero-knowledge upload tutorial with working code.

October 14, 202610 min read1,420 views
javascriptcryptographyaes-256web-cryptoprivacy

When handling sensitive personal documents, medical records, or proprietary source code, zero-knowledge client-side encryption ensures that files are encrypted in the user's browser before transmission. Even if cloud storage is breached, the stored data remains encrypted.

Cryptographic Setup: AES-256-GCM

We use AES-GCM (Galois/Counter Mode) via the browser's native Web Crypto API:

  • Provides 256-bit symmetric encryption.
  • Built-in authenticated data (integrity check) prevents tampering.
  • Requires a unique Initialization Vector (IV) for every encryption pass.
Verified Test Asset.txt

Download a standard text test file to test your browser-based AES-256 encryption and decryption functions.

Download 50KB TXT Sample →

Complete Client-Side Encryption Implementation

TypeScript
export interface EncryptedPayload {
  cipherBuffer: ArrayBuffer;
  iv: Uint8Array;
}

// 1. Generate an AES-256 Encryption Key
export async function generateAesKey(): Promise<CryptoKey> {
  return await crypto.subtle.generateKey(
    { name: 'AES-GCM', length: 256 },
    true, // extractable
    ['encrypt', 'decrypt']
  );
}

// 2. Encrypt File ArrayBuffer
export async function encryptFile(file: File, key: CryptoKey): Promise<EncryptedPayload> {
  const iv = crypto.getRandomValues(new Uint8Array(12)); // 96-bit IV recommended for AES-GCM
  const plaintextBuffer = await file.arrayBuffer();

  const cipherBuffer = await crypto.subtle.encrypt(
    {
      name: 'AES-GCM',
      iv: iv,
    },
    key,
    plaintextBuffer
  );

  return { cipherBuffer, iv };
}

// 3. Decrypt CipherBuffer
export async function decryptFile(
  cipherBuffer: ArrayBuffer,
  key: CryptoKey,
  iv: Uint8Array
): Promise<ArrayBuffer> {
  return await crypto.subtle.decrypt(
    {
      name: 'AES-GCM',
      iv: iv,
    },
    key,
    cipherBuffer
  );
}

Key Management Considerations

  • Password derivation: Never use raw user passwords directly as keys. Derive the AES key using PBKDF2 with at least 600,000 iterations and a unique salt.
  • Store the IV: The 12-byte IV is not a secret. Prepend the IV to the encrypted ciphertext before transmitting to the server.
NDL

Nguyen Dai Long

Author

Backend Lead • Distributed Systems & Cloud Edge Architecture Specialist

4+ years designing high-throughput file ingestion pipelines, database architectures, and distributed edge storage on Cloudflare R2 & AWS S3. Founder of FileDummy and the NDL Ecosystem.

Was this article helpful?

Click Like to support the author and help other developers discover this guide.

Engineering Discussion & Feedback (0)

Share benchmark results, report edge cases, or ask technical questions.

0/3000

No comments yet. Be the first developer to start the discussion!

Frequently Asked Questions

Why AES-256-GCM instead of AES-256-CBC?â–¾

AES-256-GCM provides both encryption and authenticated integrity verification in one operation — any tampering with the ciphertext is detected on decryption. AES-256-CBC only encrypts. GCM is the modern standard and is natively supported by the Web Crypto API.

Is client-side encryption safe from the server?â–¾

Client-side encryption with a user-held key achieves zero-knowledge — the server never sees plaintext. However, if the encryption code is served by the server, a compromised server could serve malicious JavaScript. True zero-knowledge requires code verification via subresource integrity or a native app.

What happens if I lose the encryption key?â–¾

The file is permanently unrecoverable. AES-256-GCM is computationally infeasible to break without the key. Always back up the key securely, derived from a password with PBKDF2 and salt.

Related Articles