encrypt-rsa
v5.0.0
Published
This is a little module use to encrypt and decrypt strings with RSA keys (public and private keys)
Maintainers
Readme
NodeRSA
NodeRSA is a library that provides easy-to-use methods for RSA encryption and decryption. It supports Node.js and browser (web) with the same API. Generate RSA key pairs, encrypt and decrypt strings with public and private keys. Ideal for secure data transmission, authentication systems, and any application requiring cryptographic security.
Installation
npm install encrypt-rsa
# OR
yarn add encrypt-rsaBreaking changes (vs 3.x)
If you are upgrading from 3.x, this release includes breaking changes. You should bump to 4.0.0 when publishing:
- Async API – All crypto methods now return Promises (sync → async). You must use
awaitor.then().- Before (3.x):
const encrypted = nodeRSA.encryptStringWithRsaPublicKey({ text, publicKey }); - After (4.x):
const encrypted = await nodeRSA.encryptStringWithRsaPublicKey({ text, publicKey });
- Before (3.x):
- Entry points – The package now has separate Node and Web builds.
main/module/typespoint to the Node build; thebrowserfield and conditionalexportspoint to the Web build. If you required a specific path (e.g.encrypt-rsa/build/index.js), update to the new entry points or use the package rootencrypt-rsa. - Buffer methods –
decryptBufferWithRsaPrivateKeynow returnsPromise<Uint8Array>(type). In Node the runtime value is still aBuffer(extendsUint8Array). PreferUint8Arrayin types; avoid relying oninstanceof Bufferin shared code.
See CHANGELOG.md for the full list of changes.
Node and Web (browser)
One package, two environments:
- Node.js: Uses the built-in
cryptomodule. All crypto methods return Promises (async API). - Browser: Uses the Web Crypto API. Same async API; bundlers resolve the web build via
exports/browserfield.
You use the same import; the correct implementation is chosen at build/runtime:
import NodeRSA from 'encrypt-rsa';- In Node (or when your bundler targets Node), you get the Node build.
- When your bundler targets the browser, you get the web build.
Browser note: In the browser build, encrypt(privateKey) and decrypt(publicKey) are not supported (Web Crypto does not support that flow) and will throw. Use encryptStringWithRsaPublicKey / decryptStringWithRsaPrivateKey for encryption and decryption.
Same interface (Node and Web)
Both the Node and Web builds expose the same class and method signatures (they implement the shared INodeRSA interface). You write the same code; only the resolved implementation changes:
- Same class:
NodeRSA - Same constructor:
(publicKey?: string, privateKey?: string, modulusLength?: number) - Same methods:
encryptStringWithRsaPublicKey,decryptStringWithRsaPrivateKey,encrypt,decrypt,createPrivateAndPublicKeys,encryptBufferWithRsaPublicKey,decryptBufferWithRsaPrivateKey - Same parameter and return types: All crypto methods return
Promise<...>; buffer methods useUint8Array(in Node,BufferextendsUint8Arrayso it works as well).
See docs/FEATURE_PARITY.md for a feature-by-feature comparison of Node vs Web (including the browser limitation for encrypt/decrypt with private/public key).
Usage
Example: Node.js
// Node (CommonJS or ESM)
import NodeRSA from 'encrypt-rsa';
const nodeRSA = new NodeRSA();
const { publicKey, privateKey } = await nodeRSA.createPrivateAndPublicKeys(2048);
const encrypted = await nodeRSA.encryptStringWithRsaPublicKey({
text: 'Secret message',
publicKey,
});
console.log('Encrypted:', encrypted);
const decrypted = await nodeRSA.decryptStringWithRsaPrivateKey({
text: encrypted,
privateKey,
});
console.log('Decrypted:', decrypted);Example: Browser (Web)
// Browser (ESM or bundled) – same API
import NodeRSA from 'encrypt-rsa';
const nodeRSA = new NodeRSA();
const { publicKey, privateKey } = await nodeRSA.createPrivateAndPublicKeys(2048);
const encrypted = await nodeRSA.encryptStringWithRsaPublicKey({
text: 'Secret message',
publicKey,
});
console.log('Encrypted:', encrypted);
const decrypted = await nodeRSA.decryptStringWithRsaPrivateKey({
text: encrypted,
privateKey,
});
console.log('Decrypted:', decrypted);When your bundler targets the browser, it resolves the web build; the code above is unchanged.
Creating an instance
const nodeRSA = new NodeRSA(publicKey?, privateKey?, modulusLength?);Generating RSA key pairs
All crypto methods return Promises. Use await or .then():
const { publicKey, privateKey } = await nodeRSA.createPrivateAndPublicKeys(modulusLength);
console.log('Public Key:', publicKey);
console.log('Private Key:', privateKey);Encrypting and decrypting strings
Encrypt with public key, decrypt with private key
const text = 'Hello, World!';
const encryptedString = await nodeRSA.encryptStringWithRsaPublicKey({ text, publicKey });
console.log('Encrypted:', encryptedString);
const decryptedString = await nodeRSA.decryptStringWithRsaPrivateKey({
text: encryptedString,
privateKey,
});
console.log('Decrypted:', decryptedString);Encrypt with private key, decrypt with public key (Node only)
In the browser build, these methods throw. In Node, they work:
const encryptedString = await nodeRSA.encrypt({ text, privateKey });
console.log('Encrypted with Private Key:', encryptedString);
const decryptedString = await nodeRSA.decrypt({ text: encryptedString, publicKey });
console.log('Decrypted with Public Key:', decryptedString);Buffer encryption (same interface: Uint8Array)
Both Node and Web use Uint8Array in the method signature. In Node, Buffer extends Uint8Array, so you can pass a Buffer as well. Return type is Promise<Uint8Array> in both environments.
// Node
const buffer = Buffer.from('This is some binary data');
// Browser (or shared code)
const buffer = new TextEncoder().encode('This is some binary data');
const encryptedBuffer = await nodeRSA.encryptBufferWithRsaPublicKey(buffer, publicKey);
const decryptedBuffer = await nodeRSA.decryptBufferWithRsaPrivateKey(
encryptedBuffer,
privateKey
);
// Node: decryptedBuffer is Buffer
// Browser: decryptedBuffer is Uint8Array
console.log(
decryptedBuffer instanceof Uint8Array
? new TextDecoder().decode(decryptedBuffer)
: decryptedBuffer.toString()
);API
NodeRSA class
Constructor
constructor(publicKey?: string, privateKey?: string, modulusLength?: number)publicKey: Optional. RSA public key (PEM).privateKey: Optional. RSA private key (PEM).modulusLength: Optional. Modulus length in bits (default 2048).
Methods (all crypto methods return Promise<...>)
| Method | Returns | Description |
|--------|---------|-------------|
| createPrivateAndPublicKeys(modulusLength?) | Promise<{ publicKey, privateKey }> | Generate RSA key pair (PEM). |
| encryptStringWithRsaPublicKey(args) | Promise<string> | Encrypt with public key. |
| decryptStringWithRsaPrivateKey(args) | Promise<string> | Decrypt with private key. |
| encrypt(args) | Promise<string> | Encrypt with private key. Node only. |
| decrypt(args) | Promise<string> | Decrypt with public key. Node only. |
| encryptBufferWithRsaPublicKey(buffer, publicKey?) | Promise<string> | Encrypt buffer; returns base64 string. |
| decryptBufferWithRsaPrivateKey(encryptedText, privateKey?) | Promise<Uint8Array> | Decrypt to buffer (same type in Node and Web). |
Parameter types
parametersOfEncrypt:{ text: string; publicKey?: string }parametersOfDecrypt:{ text: string; privateKey?: string }parametersOfEncryptPrivate:{ text: string; privateKey?: string }parametersOfDecryptPublic:{ text: string; publicKey?: string }returnCreateKeys:{ publicKey: string; privateKey: string }
Algorithm and key format
- RSA-OAEP with SHA-1 for encrypt/decrypt with public/private key (cross-compatible between Node and browser).
- Keys are PEM (SPKI for public, PKCS#8 for private). Keys generated on one side work on the other.
Use cases
Secure data transmission
// Sender
const encryptedMessage = await nodeRSA.encryptStringWithRsaPublicKey({
text: 'Sensitive data',
publicKey: recipientPublicKey,
});
// Send encryptedMessage to the recipient
// Recipient
const decryptedMessage = await nodeRSA.decryptStringWithRsaPrivateKey({
text: encryptedMessage,
privateKey: recipientPrivateKey,
});
console.log('Decrypted Message:', decryptedMessage);Authentication
const encryptedCredentials = await nodeRSA.encryptStringWithRsaPublicKey({
text: 'username:password',
publicKey: serverPublicKey,
});
const decryptedCredentials = await nodeRSA.decryptStringWithRsaPrivateKey({
text: encryptedCredentials,
privateKey: serverPrivateKey,
});
console.log('Decrypted Credentials:', decryptedCredentials);Getting started with examples
We provide practical examples for both Node.js and browser environments to help you get started quickly:
- Node.js example – Command-line demo showing key generation, encryption, and decryption
- Browser example – Interactive web interface with a UI for testing encryption/decryption
- Examples README – Detailed guide for running and understanding the examples
To run the Node.js example:
node examples/node-basic.jsTo view the browser example, open examples/browser-basic.html in your web browser.
Data size limitations
RSA encryption with OAEP padding has inherent size limitations based on the key size:
Maximum message sizes
| Key Size | Max Bytes | |----------|-----------| | 2048-bit | ~190 bytes | | 4096-bit | ~446 bytes |
Why the limitation?
RSA with OAEP padding requires overhead:
- OAEP padding scheme: 2 * hash_size + 2 bytes
- With SHA-1 (20 bytes): 2 * 20 + 2 = 42 bytes overhead
- Formula:
max_bytes = (key_size_bytes - 42 - 2) ≈ key_size_bytes / 8 - 46
Solutions for larger data
For encrypting larger messages, use hybrid encryption:
- Generate a random symmetric key (e.g., 32 bytes for AES-256)
- Encrypt the symmetric key with RSA (fits in ~190 bytes)
- Encrypt your large data with the symmetric key (no size limit)
- Send both encrypted key and encrypted data to the recipient
Example libraries:
crypto-js– Symmetric encryption with AEStweetnacl-js– Modern cryptography with libsodium- TweetNaCl.js – XChaCha20-Poly1305
Error handling & troubleshooting
Common errors and solutions
| Error | Cause | Solution |
|-------|-------|----------|
| "data too large for key size" | Message exceeds RSA capacity (~190 bytes for 2048-bit keys) | Use smaller message, larger key, or hybrid encryption |
| "Invalid public key format" | PEM key is malformed or wrong type | Verify key starts with -----BEGIN PUBLIC KEY----- |
| "Invalid private key format" | PEM key is malformed or wrong type | Verify key starts with -----BEGIN PRIVATE KEY----- |
| "Decryption failed" | Wrong private key or corrupted ciphertext | Ensure the correct private key matches the public key used for encryption |
| "Web Crypto API is not available" | Browser doesn't support crypto.subtle or not using HTTPS/localhost | Use Chrome 37+, Firefox 34+, Safari 11+, or Edge 79+; use HTTPS in production |
Key validation helpers
Use the provided validation functions to check keys before encryption:
import { isValidPEMPublicKey, isValidPEMPrivateKey, isValidPEMKey } from 'encrypt-rsa';
if (!isValidPEMPublicKey(key)) {
console.error('Invalid public key format');
}
if (!isValidPEMPrivateKey(key)) {
console.error('Invalid private key format');
}
// Check if key is either public or private
if (!isValidPEMKey(key)) {
console.error('Invalid key format');
}Debugging tips
Check key format: Ensure PEM keys have proper headers/footers
-----BEGIN PUBLIC KEY----- [base64 content] -----END PUBLIC KEY-----Verify key pair matching: The private key must match the public key
const keys = await nodeRSA.createPrivateAndPublicKeys(2048); // keys.publicKey and keys.privateKey are a matching pairTest with small messages: Start with short messages to isolate size issues
const short = 'test'; // Start here const encrypted = await nodeRSA.encryptStringWithRsaPublicKey({ text: short, publicKey });Use try-catch blocks: Always wrap crypto operations
try { const encrypted = await nodeRSA.encryptStringWithRsaPublicKey({ text, publicKey }); } catch (error) { console.error('Encryption failed:', error.message); }
Testing
The project includes tests for both the Node and web builds:
- Node tests (
tests/functionalty.node.spec.ts): Run against the Node build; cover all methods including encrypt/decrypt with private/public key and buffer operations. - Web tests (
tests/functionalty.web.spec.ts): Run against the web build; requirecrypto.subtle(Node 19+ or a browser). Skipped automatically when Web Crypto is not available.
npm testBoth suites run with npm test. Web tests are skipped when crypto.subtle is not available (e.g. Node below 19).
Documentation
API documentation is generated with Compodoc. To generate the docs (from the Node build source):
npm run docsGenerated files are written to the docs/ folder. To serve them locally:
npm run docs:serveThe docs reflect the NodeRSA class and its async API (Node and web share the same interface).
Releasing
- Changelog from commits: Run
npm run changelogto update CHANGELOG.md from conventional commits since the last tag (feat:,fix:,BREAKING CHANGE:, etc.). - Full release: Run
npm run release -- --release-as major|minor|patchto bump version, update the changelog, commit, and tag. Push tomasterto trigger the publish workflow (see .github/workflows/publish.yml).
Publish via GitHub Actions (on merge to master):
- Secret: In the repo go to Settings → Secrets and variables → Actions and add NPM_TOKEN (npm automation token with “Publish” permission).
- Trigger: Merging (or pushing) to the master branch runs the workflow: install → test → build → publish. The JS-DevTools/npm-publish action publishes only if the version in
package.jsonis greater than the latest on npm; otherwise the job succeeds but skips publishing. - Optional: To make installs reproducible in CI, commit
package-lock.json(remove it from.gitignore) and change the workflow step fromnpm installtonpm ci.
Contribution
- Fork the repository on GitHub.
- Clone your fork:
git clone [email protected]:miladezzat/encrypt-rsa.git - Create a branch:
git checkout -b feature/your-feature-name - Make your changes, then commit with a clear message.
- Push to your fork and open a pull request with a description of your changes.
Code of conduct
This project is released with a Contributor Code of Conduct. By participating you agree to abide by its terms.
Reporting issues
Please report issues via the GitHub issue tracker. Include details about the problem and your environment (OS, Node.js version, bundler, etc.).
