UNPKG

ephy-rsa

Version:

A hybrid RSA + AES encryption service using jose for large data encryption on the browser.

199 lines (132 loc) β€’ 6.03 kB
# Ephy-RSA Ephy-RSA is a lightweight encryption library for securely exchanging data between a frontend application and a backend server. It generates **ephemeral RSA key pairs** in browser memory, ensuring that sensitive data is **encrypted before transmission** and that **no keys are persistently stored**. This prevents plaintext exposure in browser network logs and enhances security. ## tl;dr Below is a **Sequence Diagram** illustrating the encryption and decryption process: ![Sequence Diagram](https://github.com/aman-eth/ephy-rsa/raw/main/images/mermaid.svg) ## πŸš€ Features - πŸ” **Ephemeral RSA Key Pairs** – No keys are stored persistently on the frontend. - πŸ”„ **Client-Side Encryption** – Data is encrypted before leaving the browser. - πŸš€ **Easy Integration** – Works seamlessly with JSON Web Encryption (JWE) using `jose`. - πŸ”’ **No Plaintext Exposure** – Data remains encrypted in transit. --- ## πŸ— Installation ```sh npm install ephy-rsa ``` OR ```sh yarn add ephy-rsa ``` --- ## πŸ›  Usage ### πŸ–₯ Frontend (Client-Side) 1️⃣ **Initialize the Library and Generate an Ephemeral Key Pair** ```javascript import RSAKeyService from "ephy-rsa"; (async () => { const rsaService = await RSAKeyService(); console.log("Public Key:", rsaService.getPublicKey()); })(); ``` 2️⃣ **Encrypt Request Payload with the Server's Public Key** ```javascript (async () => { const rsaService = await RSAKeyService(); const serverPublicKey = "your-server-public-key-in-JWK-or-PEM-format"; const encryptedData = await rsaService.encryptWithServerPublicKey( "Sensitive Data", serverPublicKey, ); console.log("Encrypted Data:", encryptedData); })(); ``` 3️⃣ **Decrypt Response Data with the Ephemeral Private Key** ```javascript (async () => { const rsaService = await RSAKeyService(); const encryptedData = "..."; // Encrypted string received from backend const decryptedData = await rsaService.decryptWithPrivateKey(encryptedData); console.log("Decrypted Data:", decryptedData); })(); ``` --- ### πŸ–₯ Backend (Server-Side) The backend should: 1. **Generate an RSA Key Pair** and expose the **public key** to the frontend. 2. **Decrypt incoming encrypted messages** using the **private key**. 3. **Encrypt responses** using **client's public key**. #### Example Backend Code (Node.js using `jose`) ```javascript import * as jose from "jose"; import fs from "fs"; // Load RSA Private Key (PEM format) const privateKeyPem = fs.readFileSync("./private.pem", "utf8"); const privateKey = await jose.importPKCS8(privateKeyPem, "RSA-OAEP"); // Load RSA Public Key (PEM format) const publicKeyPem = fs.readFileSync("./public.pem", "utf8"); const publicKey = await jose.importSPKI(publicKeyPem, "RSA-OAEP"); // Decrypt received encrypted data async function decryptData(encryptedData) { const decrypted = await jose.compactDecrypt(encryptedData, privateKey); console.log("Decrypted Data:", new TextDecoder().decode(decrypted.plaintext)); } // Encrypt responses async function encryptResponse(response, clientPublicKey) { const encrypted = await jose.compactEncrypt( JSON.stringify(response), clientPublicKey, ); console.log("Encrypted Response:", new TextDecoder().decode(encrypted)); } export { publicKey, decryptData, encryptResponse }; ``` The **public key** should be **exposed via an API endpoint**, so the frontend can retrieve it and use it for encryption. --- ## πŸ”€ API Reference ### `RSAKeyService(algorithm?: RsaHashedKeyGenParams): Promise<RSAKeyService>` Creates an instance of `RSAKeyService`. You can optionally specify an RSA key generation algorithm. ### `getPublicKey(): string | null` Returns the **Base64-encoded PEM public key** of the generated RSA key pair. ### `encryptWithServerPublicKey(data: string, jwkPublicKey: JsonWebKey | string, alg?: "RSA-OAEP" | "RSA-OAEP-256", enc?: "A128GCM" | "A256GCM"): Promise<string>` Encrypts the given data using the server’s **public key**. The key can be in **JWK** or **PEM/Base64** format. ### `decryptWithPrivateKey(encryptedData: string): Promise<string>` Decrypts the given encrypted data using the **ephemeral private key** stored in memory. --- ## πŸ”’ Security Considerations - **Frontend:** - Private keys are never stored persistently – They exist only in memory and are lost when the page refreshes. - Protect against XSS attacks – Since in-memory keys can be accessed if malicious scripts are injected. - Use HTTPS – To prevent man-in-the-middle attacks. - Implement a strong Content Security Policy (CSP) – To restrict script execution and mitigate injection risks. - **Backend:** - Store the private key securely. - Restrict access to the decryption endpoint. - Ensure proper authentication and authorization before decrypting data. --- ## πŸ“– TypeScript Interface ```typescript interface RSAKeyService { getPublicKey(): string | null; encryptWithServerPublicKey( data: string, jwkPublicKey: JsonWebKey | string, alg?: "RSA-OAEP" | "RSA-OAEP-256", enc?: "A128GCM" | "A256GCM", ): Promise<string>; decryptWithPrivateKey(encryptedData: string): Promise<string>; } ``` --- ## πŸ“š Additional Resources - **Github Repository**: [GitHub - aman-eth/ephy-rsa](https://github.com/aman-eth/ephy-rsa) - **Demo Repository**: [GitHub - aman-eth/ephy-rsa-example](https://github.com/aman-eth/ephy-rsa-example) - **Blog Article**: [Secure Client-Side Encryption with Ephemeral RSA Key Pairs: A Modern Approach](https://aman-eth.medium.com/secure-client-side-encryption-with-ephemeral-rsa-key-pairs-a-modern-approach-ed8001fce2e2) --- ## 🏷 Keywords `reactjs`, `nextjs`, `RSA`, `AES`, `encryption`, `decryption`, `hybrid`, `jose`, `cryptography`, `browser`, `secure`, `ephemeral` --- ## πŸ“ License This project is licensed under the MIT License --- ## ❀️ Contributing Contributions are welcome! Feel free to open issues or pull requests or reach out to me via GitHub Issues.