Web Client Encryption

Proof of concept demonstrating RSA-4096 asymmetric and AES-256 symmetric end-to-end encryption entirely in the browser using node-forge & CryptoJS.

⚠️ Proof of Concept — Not for production use
🔑
Asymmetric Encryption

The client encrypts a "secret" field using the server's RSA-4096 public key before submitting the form. The encrypted value is stored as %*ENCRYPTED*%(base64…). Only the server's private key (password-protected) can decrypt it.

🔄
Symmetric (PGP-like)

Simulates PGP-style encryption. Party A retrieves a pre-shared AES-256 key (delivered encrypted with their RSA public key), decrypts it with their private key, encrypts the secret field, then re-seals the AES key under Party B's RSA public key. Party B decrypts using their private key.

🏗️ Architecture
  • Backend — Rust + Axum REST API, SQLite database, per-IP rate limiting (120 req/min)
  • Frontend — Vanilla JS, node-forge (RSA-OAEP), CryptoJS (AES-256-CBC)
  • Keys — RSA-4096 PEM pairs (password-protected), AES-256 pre-shared key — generated via setup_keys.sh
  • Format — Encrypted fields stored as %*ENCRYPTED*%(base64_ciphertext)
  • AES IV — Random 16-byte IV prepended to ciphertext before base64 encoding
  • RSA padding — OAEP with SHA-256 (both hash and MGF1)
Asymmetric Encryption Flow — RSA-4096-OAEP-SHA256
BROWSER node-forge %*ENCRYPTED*% (RSA-4096-OAEP) Form Submit SecretText only PublicText → plain SERVER API Rust + Axum Serves public key Stores record as-is Serves private key (password-protected) DATABASE SQLite %*ENCRYPTED*% (stored encrypted) 1. GET public key 2. POST encrypted 3. Store 4. GET record 5. Encrypted record 6. User enters private key password → node-forge decrypts RSA-OAEP → plaintext revealed Private key fetched from server (GET /api/asym/private-key)
Symmetric (PGP-like) Encryption Flow — AES-256-CBC + RSA-4096-OAEP
PARTY A (Sender) 1. Fetch encrypted AES key 2. Decrypt with own private key 3. Encrypt SecretText (AES-256) 4. Re-seal AES key for Party B 5. POST both to server AES-256-CBC (random IV) RSA-4096-OAEP (AES key) Keys from server Password-protected SERVER / DB Stores pre-shared AES key encrypted for Party A encrypted_sym_key (RSA-sealed for Party B) encrypted_secret (AES-256 ciphertext) Serves all RSA key pairs PARTY B (Receiver) 1. Fetch record from DB 2. Decrypt AES key with own RSA private key 3. Decrypt message with AES key 4. Edit & re-encrypt with AES 5. PUT updated record Plaintext revealed ✓ Refresh to get updates from Party A GET sym-key-for-A POST encrypted record GET record PUT updated Key insight: The AES-256 key never travels in plaintext. It is always RSA-encrypted for the intended recipient. The same AES key (from server) is used, but re-sealed per transmission. IV is random per encryption.