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.
npm install encrypt-rsa
# OR
yarn add encrypt-rsaIf you are upgrading from 3.x, this release includes breaking changes. You should bump to 4.0.0 when publishing:
await or .then().const encrypted = nodeRSA.encryptStringWithRsaPublicKey({ text, publicKey });const encrypted = await nodeRSA.encryptStringWithRsaPublicKey({ text, publicKey });main/module/types point to the Node build; the browser field and conditional exports point 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 root encrypt-rsa.decryptBufferWithRsaPrivateKey now returns Promise<Uint8Array> (type). In Node the runtime value is still a Buffer (extends Uint8Array). Prefer Uint8Array in types; avoid relying on instanceof Buffer in shared code.See CHANGELOG.md for the full list of changes.
One package, two environments:
crypto module. All crypto methods return Promises (async API).exports / browser field.You use the same import; the correct implementation is chosen at build/runtime:
Example :import NodeRSA from 'encrypt-rsa';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.
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:
NodeRSA(publicKey?: string, privateKey?: string, modulusLength?: number)encryptStringWithRsaPublicKey, decryptStringWithRsaPrivateKey, encrypt, decrypt, createPrivateAndPublicKeys, encryptBufferWithRsaPublicKey, decryptBufferWithRsaPrivateKeyPromise<...>; buffer methods use Uint8Array (in Node, Buffer extends Uint8Array so 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).
// 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);// 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.
const nodeRSA = new NodeRSA(publicKey?, privateKey?, modulusLength?);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);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);In the browser build, these methods throw. In Node, they work:
Example :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);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()
);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).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). |
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 }// 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);const encryptedCredentials = await nodeRSA.encryptStringWithRsaPublicKey({
text: 'username:password',
publicKey: serverPublicKey,
});
const decryptedCredentials = await nodeRSA.decryptStringWithRsaPrivateKey({
text: encryptedCredentials,
privateKey: serverPrivateKey,
});
console.log('Decrypted Credentials:', decryptedCredentials);We provide practical examples for both Node.js and browser environments to help you get started quickly:
To run the Node.js example:
Example :node examples/node-basic.jsTo view the browser example, open examples/browser-basic.html in your web browser.
RSA encryption with OAEP padding has inherent size limitations based on the key size:
| Key Size | Max Bytes |
|---|---|
| 2048-bit | ~190 bytes |
| 4096-bit | ~446 bytes |
RSA with OAEP padding requires overhead:
max_bytes = (key_size_bytes - 42 - 2) ≈ key_size_bytes / 8 - 46For encrypting larger messages, use hybrid encryption:
Example libraries:
crypto-js – Symmetric encryption with AEStweetnacl-js – Modern cryptography with libsodium| 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 |
Use the provided validation functions to check keys before encryption:
Example :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');
}Check key format: Ensure PEM keys have proper headers/footers
Example :-----BEGIN PUBLIC KEY-----
[base64 content]
-----END PUBLIC KEY-----Verify key pair matching: The private key must match the public key
Example :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
Example :const short = 'test'; // Start here
const encrypted = await nodeRSA.encryptStringWithRsaPublicKey({ text: short, publicKey });Use try-catch blocks: Always wrap crypto operations
Example :try {
const encrypted = await nodeRSA.encryptStringWithRsaPublicKey({ text, publicKey });
} catch (error) {
console.error('Encryption failed:', error.message);
}The project includes tests for both the Node and web builds:
tests/functionalty.node.spec.ts): Run against the Node build; cover all methods including encrypt/decrypt with private/public key and buffer operations.tests/functionalty.web.spec.ts): Run against the web build; require crypto.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).
API documentation is generated with Compodoc. To generate the docs (from the Node build source):
Example :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).
npm run changelog to update CHANGELOG.md from conventional commits since the last tag (feat:, fix:, BREAKING CHANGE:, etc.).npm run release -- --release-as major|minor|patch to bump version, update the changelog, commit, and tag. Push to master to trigger the publish workflow (see .github/workflows/publish.yml).Publish via GitHub Actions (on merge to master):
package.json is greater than the latest on npm; otherwise the job succeeds but skips publishing.package-lock.json (remove it from .gitignore) and change the workflow step from npm install to npm ci.git clone git@github.com:miladezzat/encrypt-rsa.gitgit checkout -b feature/your-feature-nameThis project is released with a Contributor Code of Conduct. By participating you agree to abide by its terms.
Please report issues via the GitHub issue tracker. Include details about the problem and your environment (OS, Node.js version, bundler, etc.).