npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

crypto-secure

v1.2.2

Published

Hybrid encryption (RSA + AES-GCM) with Express middleware for automatic request/response encryption

Readme

crypto-secure

Hybrid encryption library (RSA-2048 + AES-256-GCM) with Express middleware and browser client for automatic end-to-end encrypted communication.

Drop it into your Express backend + React/Vue/any frontend and get end-to-end encryption with 4 lines of code — no crypto expertise required.


Why crypto-secure? (The Problem It Solves)

In today's era of mass surveillance, data breaches, and API sniffing, HTTPS alone is not enough:

| Threat | HTTPS Protection | crypto-secure Protection | |---|---|---| | TLS termination at load balancer | ❌ — plaintext inside your network | ✅ — end-to-end encrypted | | Cloud provider / hosting access | ❌ — can read your data | ✅ — only your keys can decrypt | | Man-in-the-middle at proxy layer | ❌ — TLS is terminated | ✅ — RSA + AES-GCM envelope | | Database leak (server-side) | ❌ — logged after decryption | ✅ — never stored in plaintext | | Insider access to server memory | ❌ — plaintext in process | ✅ — encrypted until handled |

What makes it unique: Unlike libraries that just provide raw crypto functions, crypto-secure gives you drop-in middleware that makes every request and response automatically encrypted — your route handlers don't change at all.


Features

  • 🔐 Hybrid encryption — RSA-2048 OAEP SHA-256 wraps AES-256 keys, AES-256-GCM encrypts payloads. Best of both worlds: fast symmetric encryption with convenient asymmetric key exchange.
  • ✅ Authenticated encryption (AEAD) — AES-GCM prevents tampering, padding oracle attacks, and chosen-ciphertext attacks (unlike AES-CBC).
  • 📦 Payload compression — Plaintext is gzip-compressed before encryption, reducing wire size by 50–85% for typical API payloads without any security tradeoff.
  • 🔄 Express middlewarecs.middleware({ privateKey }) auto-decrypts incoming req.body and auto-encrypts outgoing res.json(). Zero changes to your route handlers.
  • 🌐 Browser client — Full Web Crypto API implementation. Works with React, Vue, Angular, vanilla JS. No node-forge needed on the client.
  • 🪝 Axios interceptors — Built-in request/response interceptors for transparent encryption. Set once, forget about it.
  • 🔑 Fresh keys per message — Every encryption generates a new random AES-256 key and 12-byte IV. Perfect forward secrecy at the message level.
  • 📎 AAD (Additional Authenticated Data) — Bind context metadata (user ID, API version, timestamp) to the ciphertext so it can't be replayed in a different context.
  • 💾 Key persistencegenerateKeyPair("./keys") writes .pem files to disk automatically.
  • 🍪 Cookie-based key caching — Browser client caches the server's public key in a SameSite=Lax cookie for 24 hours, avoiding re-fetch on page reload.
  • 🔒 Route-level encryption — Simple base64 route/value encryption for URL params, tokens, and short strings.
  • 📦 Zero extra dependencies in Node — Only node-forge at runtime; compression uses Node's built-in zlib. Browsers use native CompressionStream API.
  • 🧩 Framework-agnostic frontend — ES Module export works with any bundler (Vite, Webpack, esbuild, etc.).

Comparison: Traditional vs crypto-secure

| Aspect | Traditional Approach | crypto-secure | |---|---|---| | Setup time | Days (select algos, implement key exchange, write middleware) | Minutes | | Lines of code | 30–100+ per project | 4 lines (backend) / 6 lines (frontend) | | Encryption type | Manual per-endpoint | Automatic via middleware | | Algorithm | AES-CBC (vulnerable) or raw RSA (slow) | AES-256-GCM (authenticated, fast) | | Key exchange | Hardcoded keys or complex PKI | RSA-2048 OAEP SHA-256 | | Tamper detection | None (CBC) | Built-in (GCM authentication tag) | | Wire size | Full plaintext + padding | Compressed + encrypted (50–85% smaller for typical payloads) | | Frontend support | Usually none provided | Web Crypto API (React, Vue, etc.) | | Key storage | Manual file management | Auto-save .pem or in-memory | | Security rating | ~6/10 (CBC, no auth, weak MGF) | ~9/10 (GCM, OAEP SHA-256, AAD) |


How It Works (Protocol)

Wire Format

Every encrypted payload on the wire uses this JSON envelope:

{
  "encryptedAESKey": "<base64 RSA-wrapped AES-256 key>",
  "delta": "<base64 12-byte IV>",
  "tag": "<base64 16-byte GCM authentication tag>",
  "encryptedData": "<base64 AES-GCM ciphertext>",
  "aad": "<base64 additional authenticated data (optional)>"
}

Encryption Flow

Plaintext (any JSON)
    │
    ▼
JSON.stringify()
    │
    ▼
gzip compress (NEW — reduces wire size 50–85%)
    │
    ▼
AES-256-GCM encrypt with random key + IV
    │
    ├── ciphertext (→ encryptedData)
    └── auth tag (→ tag)
    │
AES key → RSA-2048 OAEP encrypt with recipient's public key (→ encryptedAESKey)
    │
    ▼
Base64-encode all binary fields → JSON envelope

Decryption Flow

JSON envelope
    │
    ▼
Base64-decode all fields
    │
    ▼
RSA-2048 OAEP decrypt → recover AES key
    │
    ▼
AES-256-GCM decrypt + verify authentication tag
    │
    ▼
gunzip decompress
    │
    ▼
JSON.parse → original payload

Key Exchange Flow

Client (Browser)                         Server (Express)
┌─────────────────┐                     ┌────────────────────────┐
│  generateKeyPair()│                     │  generateKeyPair()     │
│  → client keys   │                     │  → server keys         │
└────────┬────────┘                     └───────────┬────────────┘
         │                                           │
         │  GET /.well-known/encryption-key          │
         │ ←────────────────────────────────── publicKey
         │                                           │
         │  POST /api/data                           │
         │  Header: x-encryption-key (← client pub)   │
         │  Body: encrypted envelope                  │
         │ ─────────────────────────────────────────→ │
         │                                           │  decrypt with
         │                                           │  server privateKey
         │                                           │  → req.body
         │                                           │
         │  Response: encrypted envelope              │
         │ ←───────────────────────────────────────── │  encrypt with
         │                                           │  client publicKey
         │  decrypt with                              │
         │  client privateKey                         │
         │  → response.data                           │

Backend Usage (Express)

Install

npm install crypto-secure

Minimum Setup (4 lines)

const cs = require("crypto-secure");
const keypair = cs.generateKeyPair("./keys");
app.use(express.json());
app.use(cs.middleware({ privateKey: keypair.privateKey }));

Your existing routes stay completely unchanged:

app.post("/api/data", (req, res) => {
  // req.body is already decrypted automatically
  console.log(req.body);
  res.json({ received: req.body, ok: true });
  // response is auto-encrypted with client's public key
});

Production Setup (key persistence)

const fs = require("fs");
const cs = require("crypto-secure");

const keyFile = "./keys/server-key.json";
let keypair;
if (fs.existsSync(keyFile)) {
  keypair = JSON.parse(fs.readFileSync(keyFile, "utf8"));
} else {
  keypair = cs.generateKeyPair("./keys");
  fs.writeFileSync(keyFile, JSON.stringify(keypair));
}

app.use(express.json());
app.use(cs.middleware({ privateKey: keypair.privateKey }));

// Expose your public key for clients
app.get("/.well-known/encryption-key", (req, res) => {
  res.json({ publicKey: keypair.publicKey });
});

Frontend Usage (React / Browser)

Install

npm install crypto-secure

Setup (6 lines)

import * as cs from "crypto-secure/client";

const serverKey = await cs.fetchServerPublicKey("/.well-known/encryption-key");
const clientKeys = await cs.generateKeyPair();

axios.defaults.headers.common["x-encryption-key"] =
  cs.getClientHeader(clientKeys.publicKey)["x-encryption-key"];

Encrypt a request

const encrypted = await cs.encrypt(payload, serverKey);
// → { encryptedAESKey, delta, tag, encryptedData }
axios.post("/api/data", encrypted);

Decrypt a response

const res = await axios.post("/api/data", payload);
const decrypted = await cs.decrypt(res.data, clientKeys.privateKey);

Automatic encryption via Axios interceptors

axios.interceptors.request.use(cs.createAxiosRequestInterceptor(serverKey));
axios.interceptors.response.use(
  ...cs.createAxiosResponseInterceptor(clientKeys.privateKey)
);
// All requests/responses are now automatically encrypted — zero manual work

Route-level encryption (URL params, tokens, etc.)

const enc = cs.encryptRoute("sensitive-value"); // base64 encoded
const dec = cs.decryptRoute(enc);               // back to original

API Reference

Server-side (crypto-secure)

| Function | Description | |---|---| | generateKeyPair(saveTo?) | Generate RSA-2048 keypair. If saveTo is a path, writes public.pem and private.pem | | encrypt(payload, publicKey, aad?) | Hybrid encrypt with RSA + AES-GCM. Payload is gzip-compressed before encryption | | decrypt(encryptedPayload, privateKey) | Hybrid decrypt. Ciphertext is gunzip-decompressed after decryption | | isEncrypted(obj) | Check if an object is an encrypted payload (duck-type check on envelope fields) | | generateAESKey() | Generate a random 32-byte AES-256 key | | generateIV() | Generate a random 12-byte IV for GCM | | middleware(options) | Express middleware factory. Options: { privateKey, publicKeyHeader, decryptBody, encryptResponse } | | Client | Node.js client class for programmatic encryption/decryption with key management |

Browser-side (crypto-secure/client)

| Function | Description | |---|---| | generateKeyPair() | Generate RSA-2048 keypair using Web Crypto API | | getClientHeader(publicKeyPem) | Returns { "x-encryption-key": "..." } header (base64 DER without PEM framing) | | fetchServerPublicKey(url) | Fetch server's public key from endpoint. Cached in memory + cookie for 24h | | clearServerPublicKeyCache() | Clear in-memory and cookie cache | | encrypt(payload, serverPublicKey) | Hybrid encrypt for requests (with gzip compression) | | decrypt(encryptedPayload, clientPrivateKey) | Hybrid decrypt for responses (with gunzip decompression) | | encryptRoute(value) | Base64-encode a route/param value (padding-stripped) | | decryptRoute(value) | Base64-decode a route/param value | | createAxiosRequestInterceptor(serverKey) | Axios request interceptor — auto-encrypts all outgoing data | | createAxiosResponseInterceptor(clientPrivateKey) | Axios response interceptor — auto-decrypts all incoming responses |

Middleware Options

| Option | Type | Default | Description | |---|---|---|---| | privateKey | string (PEM) | required | Server's RSA private key for decrypting request bodies | | publicKeyHeader | string | "x-encryption-key" | HTTP header where client sends its public key | | decryptBody | boolean | true | Auto-decrypt req.body if it's an encrypted envelope | | encryptResponse | boolean | true | Auto-encrypt res.json() responses with client's public key |


Payload Compression Impact

Since the library now gzip-compresses plaintext before encryption, here's the expected reduction in encryptedData size:

| JSON Payload Size | Before (base64) | After (compressed + base64) | Reduction | |---|---|---|---| | 100 bytes | ~133 chars | ~133 chars | ~0% (gzip overhead) | | 1 KB | ~1,365 chars | ~410 chars | ~70% | | 10 KB | ~13,650 chars | ~2,730 chars | ~80% | | 100 KB | ~136,500 chars | ~20,500 chars | ~85% | | 1 MB | ~1.37M chars | ~137K chars | ~90% |

Note: The fixed envelope overhead (encryptedAESKey + delta + tag ≈ 384 base64 chars) is constant. Compression provides the most benefit for payloads above 500 bytes. Very small or already-compressed payloads see minimal gain.


Security Details

Algorithms

| Component | Algorithm | Parameters | |---|---|---| | Symmetric encryption | AES-256-GCM | 256-bit key, 96-bit IV, 128-bit authentication tag | | Key wrapping | RSA-OAEP | 2048-bit key, SHA-256 hash, SHA-256 MGF1 | | Compression | gzip (deflate) | Applied before encryption, zero security impact |

Security Properties

  • AEAD (Authenticated Encryption with Associated Data) — GCM provides both confidentiality and integrity. Any tampering with ciphertext is detected on decryption.
  • Randomness per message — Every encrypt() call generates a fresh AES key and IV via cryptographic RNG.
  • Key encapsulation — The AES key is never transmitted in plaintext; it's wrapped with RSA-2048 OAEP.
  • Padding oracle immunity — GCM is not vulnerable to padding oracle attacks (unlike CBC).
  • AAD binding — Optional additional data is cryptographically bound to the ciphertext. Modifying AAD invalidates the tag.
  • No plaintext in transit — The only data on the wire is the RSA-wrapped AES key, IV, authentication tag, and encrypted ciphertext.

What crypto-secure does NOT protect against

  • Side-channel attacks (timing, power analysis, cache) — not designed for HSM-level security
  • Quantum computing — RSA-2048 and AES-256 are not quantum-resistant. Post-quantum migration is a future concern
  • Compromised private keys — if your private key is stolen, all past traffic can be decrypted (no forward secrecy at the RSA level)
  • Client-side XSS — an attacker with JS execution can read plaintext after decryption

Architecture

                         ┌──────────────────────────────────┐
                         │         Shared Protocol           │
                         │   RSA-2048 OAEP SHA-256          │
                         │   AES-256-GCM (authenticated)    │
                         │   gzip compression before encrypt│
                         │   Base64 wire encoding            │
                         └──────────────────────────────────┘
                                    ↕
          ┌──────────────────────────┴──────────────────────────┐
          ▼                                                      ▼
┌─────────────────────────┐                    ┌──────────────────────────┐
│   Node.js (server)      │                    │  Browser (client)        │
│                         │                    │                          │
│  crypto-secure.js       │                    │  client-browser.mjs      │
│  └─ Core crypto engine  │                    │  └─ Web Crypto API       │
│  └─ node-forge backend  │                    │  └─ CompressionStream    │
│  └─ zlib compression    │                    │  └─ Cookie caching       │
│                         │                    │                          │
│  middleware.js           │                    │  Axios interceptors      │
│  └─ Express middleware   │                    │  └─ Request auto-encrypt │
│  └─ Auto decrypt req     │                    │  └─ Response auto-decrypt│
│  └─ Auto encrypt res     │                    │                          │
│                         │                    │  encryptRoute()          │
│  client.js              │                    │  decryptRoute()          │
│  └─ Node.js client class│                    │                          │
└─────────────────────────┘                    └──────────────────────────┘

Core Files

| File | Purpose | |---|---| | crypto-secure.js | Core hybrid encryption engine. UMD format — works in Node.js, AMD, and browser globals. Uses node-forge for RSA, AES-GCM, and PEM encoding, and zlib for gzip compression. | | middleware.js | Express middleware factory. Decrypts req.body, encrypts res.json(), handles PEM key reconstruction from HTTP headers. | | client.js | Node.js client class with key pair management and helper methods for request/response encryption. | | client-browser.mjs | Browser client as an ES Module. Uses Web Crypto API (crypto.subtle) and CompressionStream for native browser compression. Includes Axios interceptor factories and cookie-based key caching. | | index.js | Package entry point. Aggregates all modules and attaches .middleware and .Client to the main export. |


Package Contents

crypto-secure/
├── index.js                # Package entry (CommonJS)
├── crypto-secure.js        # Core crypto engine (UMD)
├── middleware.js            # Express middleware (CommonJS)
├── client.js               # Node.js client (CommonJS)
├── client-browser.mjs      # Browser client (ES Module)
├── CONTRIBUTING.md         # Contribution guidelines
├── CODE_OF_CONDUCT.md      # Code of conduct
├── LICENSE                 # MIT license
├── package.json
└── README.md

Requirements

  • Node.js ≥ 12.x (uses Buffer, zlib, require)
  • Express 4.x or 5.x (peer dependency, only needed for middleware)
  • Browsers: Chrome 80+, Firefox 110+, Safari 16.4+, Edge 80+ (requires CompressionStream and Web Crypto API support)

License

MIT License — see LICENSE for details.