A hybrid RSA + AES encryption service using jose for large data encryption on the browser.
npm install ephy-rsaEphy-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.
Below is a Sequence Diagram illustrating the encryption and decryption process:
- π 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.
---
``sh`
npm install ephy-rsa
OR
`sh`
yarn add ephy-rsa
---
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);
})();
`
---
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.
---
Creates an instance of RSAKeyService. You can optionally specify an RSA key generation algorithm.
Returns the Base64-encoded PEM public key of the generated RSA key pair.
Encrypts the given data using the serverβs public key. The key can be in JWK or PEM/Base64 format.
Decrypts the given encrypted data using the ephemeral private key stored in memory.
---
- 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 RSAKeyService {
getPublicKey(): string | null;
encryptWithServerPublicKey(
data: string,
jwkPublicKey: JsonWebKey | string,
alg?: "RSA-OAEP" | "RSA-OAEP-256",
enc?: "A128GCM" | "A256GCM",
): Promise
decryptWithPrivateKey(encryptedData: string): Promise
}
---
- Github Repository: GitHub - aman-eth/ephy-rsa
- Demo Repository: GitHub - aman-eth/ephy-rsa-example
- Blog Article: Secure Client-Side Encryption with Ephemeral RSA Key Pairs: A Modern Approach
---
reactjs, nextjs, RSA, AES, encryption, decryption, hybrid, jose, cryptography, browser, secure, ephemeral`
---
This project is licensed under the MIT License
---
Contributions are welcome! Feel free to open issues or pull requests or reach out to me via GitHub Issues.