Zero-knowledge upload via AES-GCM-256 + PBKDF2
Every queued file is encrypted in the browser with AES-GCM-256 before upload begins; your ASP.NET Core server only ever sees ciphertext. Decryption requires the password and the per-file salt + IV emitted as X-Mu-Encryption-* headers. Compliance-friendly default for HIPAA / GDPR confidentiality workflows. None of Uppy, FilePond, Dropzone, or FineUploader offer this built-in.
What you should see: each file is encrypted in the browser with AES-GCM before any bytes leave the page, so the server only ever receives ciphertext. Auto-upload is off so you can see the encryption step happen first.
The password never leaves the browser. The server only sees ciphertext + the salt & IV needed to decrypt on retrieval.
Drag & drop files here, or paste from clipboard
Try it: drop a file, hit upload, inspect the request body in DevTools -> Network. The payload is unintelligible binary, plus six request headers record the algorithm + salt + IV + iterations + original filename + original size.
Configuration
CoreUpload.create('#uploader', {
uploadUrl: '/api/upload/upload',
encrypt: true, // or 'aes-gcm-256' encryptPassword: 'shhh',
encryptIterations: 200000 // PBKDF2 default
}); Server-side decryption (.NET 8)
using System.Security.Cryptography;
// Read salt + IV + iter from request headers
var salt = Base64UrlDecode(req.Headers["X-Mu-Encryption-Salt"]);
var iv = Base64UrlDecode(req.Headers["X-Mu-Encryption-IV"]);
var iter = int.Parse(req.Headers["X-Mu-Encryption-Iter"]);
using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iter, HashAlgorithmName.SHA256);
var key = pbkdf2.GetBytes(32);
using var aes = new AesGcm(key, 16);
var ciphertext = encryptedBytes.AsSpan(0, encryptedBytes.Length - 16);
var tag = encryptedBytes.AsSpan(encryptedBytes.Length - 16);
var plaintext = new byte[ciphertext.Length];
aes.Decrypt(iv, ciphertext, tag, plaintext); Algorithm details
- Cipher: AES-256-GCM (authenticated encryption - tamper-evident).
- KDF: PBKDF2-HMAC-SHA256, 200,000 iterations by default.
- Salt: 128-bit random per file. Never reused.
- IV (nonce): 96-bit random per file. Never reused with the same key.
- Auth tag: 128-bit GCM tag appended to ciphertext.
Safety
- Insecure context: on
http://, the encrypt task fails fast - no silent fallback to cleartext. - Missing password: queued files fail with a clear error before any byte leaves the browser.
- Resume: when paired with
persistState, encryption is preserved - the ciphertext file is what gets persisted.