JSEncrypt: RSA encryption in the browser and Node.js without dependencies
A tiny (18.5 kB gzip), zero-dependency, Javascript library to perform OpenSSL RSA Encryption, Decryption, and Key Generation.
At a glance
- What is it?
- JSEncrypt is a zero-dependency JavaScript library for OpenSSL-compatible RSA encryption, decryption and key generation. It is small, it works in browsers and Node.js, and its main constraint is the one RSA always imposes: you can only encrypt short payloads.
- Who is it for?
- Use JSEncrypt when you need RSA encryption or decryption in JavaScript with no dependencies and PEM keys generated by OpenSSL, for example encrypting a short session key or a password field in a browser before transport. Do not use it as a general-purpose symmetric cipher for large payloads, and do not rely on its in-browser key generation for production keys, since the README itself recommends OpenSSL for that.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 175 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What JSEncrypt solves, and for whom
JSEncrypt exists for the case where a JavaScript application needs to perform RSA operations directly, without pulling in a cryptography stack. The README describes it as a tiny, zero dependency library for OpenSSL RSA encryption, decryption and key generation in both the browser and Node.js, and the package.json confirms the dependency list is empty. That combination is the selling point: if you are shipping a browser bundle and you only need to encrypt a short value with a public key, adding a full crypto toolkit is a lot of code for one operation.
The audience is therefore narrow and specific. Front-end developers who receive a PEM public key from a backend and must encrypt a password or a symmetric key before sending it. Node.js developers who need to decrypt that value with the matching private key, or who need to verify that an OpenSSL-generated key pair behaves as expected. People who generate key pairs with the openssl command line and want the JavaScript side to read the same PEM format. The library is not aimed at anyone who needs bulk encryption, signing workflows, or algorithm agility, because it does one thing.
How the RSA mechanism works inside JSEncrypt
The README states that JSEncrypt is built on Tom Wu's jsbn library without modifying the core algorithms. That matters more than it sounds. jsbn is a pure JavaScript big-integer implementation, which is why the library can do modular exponentiation in a browser without WebCrypto and why it has no dependencies. The cost is that the arithmetic runs in JavaScript rather than in native code, so key generation and decryption are slower than a native implementation would be. The README acknowledges this indirectly by recommending asynchronous key generation for larger keys.
Data flow is straightforward and stateless. You construct a JSEncrypt instance, load a key with setPublicKey or setPrivateKey, then call encrypt or decrypt. The README notes that when you set a private key, the public key is automatically derived from it, so a single setPrivateKey call is enough for both directions. Keys are PEM strings, the same format OpenSSL writes, which is why the openssl genrsa and openssl rsa -pubout commands in the README produce something the library accepts directly. There is no key store, no session state and no protocol layer: the library converts PEM to its internal representation and applies textbook RSA with the padding scheme it implements.
Installing JSEncrypt from npm or a CDN and encrypting your first string
Installation is a single package with no transitive dependencies, so npm and yarn both work and nothing else lands in your tree. The README gives both commands.
npm install jsencryptIf you prefer not to bundle it, the README documents a CDN build that exposes JSEncrypt as a browser global, which is the path the related search term jsencrypt cdn refers to.
<script src="https://cdn.jsdelivr.net/npm/jsencrypt@latest/bin/jsencrypt.min.js"></script>For module users, the README shows an ES6 import and a CommonJS require. The package.json exports map confirms both: the import condition points at lib/index.js and the require condition at bin/jsencrypt.min.js, with TypeScript types at lib/index.d.ts.
import { JSEncrypt } from 'jsencrypt';Now generate a key pair with OpenSSL, which the README calls the recommended route for production. Two commands produce private.pem and public.pem.
openssl genrsa -out private.pem 2048
openssl rsa -pubout -in private.pem -out public.pemWith the public key in hand, encryption is one call. Note that encrypt returns a base64 string, and decrypt returns the original text or false when the key does not match.
const crypt = new JSEncrypt();
crypt.setPublicKey(publicKeyString);
const encrypted = crypt.encrypt('Hello, World!');To read that value back, set the private key on another instance and call decrypt. The README's own example prints the original, the ciphertext and the decrypted value, and asserts that the original and decrypted strings are equal.
The payload size limit is the constraint that shapes your design
The README's Basic Usage section encrypts the string Hello, World! and stops there. It does not document a maximum plaintext length, and that silence is the thing to plan around, because RSA with the padding JSEncrypt implements cannot encrypt arbitrary data. A 2048-bit key gives you a small fixed number of bytes per operation, far less than a typical JSON body. Anyone who tries to encrypt a large object and finds that encrypt returns false has hit this boundary, and the README does not walk them through it.
The practical consequence is a two-step protocol: generate a symmetric key, encrypt the payload with that key using a cipher JSEncrypt does not provide, then encrypt the symmetric key with RSA and send both. JSEncrypt covers only the second half. If your design assumed a single call that encrypts a whole request, this is the wrong tool, and no configuration flag changes that.
There is a second limitation in the same area. The README distinguishes OpenSSL key generation, which it recommends, from JavaScript key generation, which it calls convenient but less secure and suitable for testing, demos or non-critical applications. The in-browser generator defaults to 1024 bits, and the README's own key-size list labels 512-bit as only for testing and 1024-bit as the default with basic security. So the convenient path also produces the weaker keys unless you explicitly pass default_key_size: 2048. The README does not document rollback, key rotation or how to detect a mismatched key pair beyond the boolean result of decrypt.
JSEncrypt versus CryptoJS and the Web Crypto API
CryptoJS is the comparison the search data keeps returning, and the difference is scope. CryptoJS is a general cipher library: symmetric ciphers, hashing, encoding utilities. JSEncrypt does RSA and only RSA. If your task is AES encryption of a payload, JSEncrypt has nothing to offer and CryptoJS does. If your task is encrypting a short value with an OpenSSL PEM public key, JSEncrypt is the smaller dependency, and the README's zero-dependency claim is verifiable in package.json.
The Web Crypto API is the other alternative, and it is built into the platform, so it adds nothing to your bundle. The trade-off is API shape and environment support: Web Crypto is asynchronous and returns promises, while JSEncrypt offers both synchronous and asynchronous patterns, which is why the README lists synchronous execution as a benefit. Web Crypto also does not accept PEM strings directly, so you would import the key material yourself. JSEncrypt's setPublicKey takes the PEM text as-is. If you are already targeting environments with Web Crypto and can work asynchronously, the platform API removes a dependency entirely; JSEncrypt's advantage is the PEM-in, base64-out interface and the option of a synchronous call.
Maintenance, licence and the cost of upgrading
The repository is not archived, and the last push was on 2026-04-08. The package.json lists version 3.5.4. There is no changelog in the retrieved material and no release notes were returned, so upgrade planning has to come from the source tree rather than from a migration document.
The build is heavier than the runtime. The scripts run tsc, a second tsc pass against tsconfig-def.json for type definitions, and webpack, with separate dev, test and prod configurations. The published files are limited to bin and lib, so consumers get the compiled output, not the TypeScript source. That means a version bump can change the shape of lib/index.d.ts, and TypeScript users will feel it at compile time rather than at runtime. The devDependencies pin TypeScript ^5.9.2 and webpack ^5.101.0, which tells you the toolchain is current as of that push.
On licensing, the README carries an MIT badge and the repository ships LICENSE.txt, but the repository metadata reports the licence as NOASSERTION, meaning the platform did not classify it. Those two signals disagree, and the badge is not the authoritative text. Read LICENSE.txt before you depend on the terms; this is a description of what the files say, not legal advice.
Editorial conclusion
Use JSEncrypt when you need RSA encryption or decryption in JavaScript with no dependencies and PEM keys generated by OpenSSL, for example encrypting a short session key or a password field in a browser before transport. Do not use it as a general-purpose symmetric cipher for large payloads, and do not rely on its in-browser key generation for production keys, since the README itself recommends OpenSSL for that. Before adopting it, verify the key size you pass to default_key_size and check what the documentation says about the maximum plaintext length for that size, because that limit, not the library, will shape your protocol.
Frequently asked questions
How do I use JSEncrypt in Node.js?
Install it with npm install jsencrypt, then require it or import the JSEncrypt class. The package.json exports map resolves require to bin/jsencrypt.min.js and import to lib/index.js, so both module systems work. Set a key with setPublicKey or setPrivateKey before calling encrypt or decrypt.
Why do I get "jsencrypt is not defined"?
That error means the global was never created, which happens when the script tag did not load or when you are in a module context that does not expose globals. The README shows the CDN script tag creating a JSEncrypt global, and separately shows import and require for module users. Pick one loading path and match the reference style to it.
Why does JSEncrypt throw "jsencrypt is not a constructor"?
The README uses two forms: import { JSEncrypt } from 'jsencrypt' for ES6 modules and const JSEncrypt = require('jsencrypt') for CommonJS, and both are then called with new. Calling the module object itself instead of the exported class produces this error. The package.json exports map is the reference for which file each form resolves to.
How does JSEncrypt compare with CryptoJS?
JSEncrypt performs RSA encryption, decryption and key generation only, and the README describes it as zero dependency. CryptoJS covers symmetric ciphers and hashing as well. If you only need to encrypt a short value with an OpenSSL PEM public key, JSEncrypt is the smaller choice; for symmetric encryption you need a different library.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/travist-jsencrypt)