About RSA Encrypt
RSA encryption is asymmetric: the public key encrypts and only the matching private key can decrypt. That asymmetry is what lets you publish the encrypting key freely - anyone can send you something confidential without any prior shared secret, which symmetric ciphers like AES cannot do.
The catch, and the reason most people end up on a page like this, is size. RSA operates on numbers smaller than its modulus, so a 2048-bit key can encrypt at most 190 bytes when using OAEP with SHA-256. It is a key-transport mechanism, not a bulk cipher.
The padding scheme is not optional decoration. Textbook RSA is deterministic and trivially attackable; OAEP adds randomness and structure so that encrypting the same message twice gives different ciphertexts and chosen-ciphertext attacks are prevented. Use OAEP with SHA-256 unless an existing system forces PKCS#1 v1.5 on you.
For anything larger than the size limit, use hybrid encryption: generate a random AES key, encrypt the payload with AES-GCM, and encrypt only the AES key with RSA. That is exactly what TLS and PGP do, and there is a complete example in the code section below.
Encryption here happens in your browser via the Web Crypto API - keys and plaintext are never uploaded.
How to use the RSA Encrypt
- Paste the recipient's RSA public key in PEM form, beginning -----BEGIN PUBLIC KEY-----.
- Enter the plaintext. Keep it under the size limit for the key - 190 bytes for a 2048-bit key with OAEP/SHA-256.
- Encrypt. The ciphertext is exactly as long as the modulus (256 bytes for a 2048-bit key) and is shown Base64-encoded.
- Send the Base64 ciphertext to the holder of the private key, and tell them the padding scheme and hash you used.
Examples
-
Short message
hello
RSA Encrypt in code
The same operation this tool performs, in the languages you are most likely to need it.
// Generate a key pair
const kp = await crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]), // 65537
hash: "SHA-256",
},
true,
["encrypt", "decrypt"]
);
const ct = await crypto.subtle.encrypt(
{ name: "RSA-OAEP" }, kp.publicKey,
new TextEncoder().encode("short secret")
);
const pt = await crypto.subtle.decrypt({ name: "RSA-OAEP" }, kp.privateKey, ct);
console.log(new TextDecoder().decode(pt));
// Export: Web Crypto uses SPKI for public keys, PKCS#8 for private.
const spki = await crypto.subtle.exportKey("spki", kp.publicKey);
import { generateKeyPairSync, publicEncrypt, privateDecrypt, constants } from "node:crypto";
const { publicKey, privateKey } = generateKeyPairSync("rsa", {
modulusLength: 2048,
publicKeyEncoding: { type: "spki", format: "pem" },
privateKeyEncoding: { type: "pkcs8", format: "pem" },
});
const opts = {
padding: constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256", // must match on both sides
};
const ct = publicEncrypt({ key: publicKey, ...opts }, Buffer.from("short secret"));
const pt = privateDecrypt({ key: privateKey, ...opts }, ct);
console.log(pt.toString());
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
pad = padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
)
ct = public_key.encrypt(b"short secret", pad)
print(private_key.decrypt(ct, pad))
pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
)
# Generate a 2048-bit key pair
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem
openssl rsa -in private.pem -pubout -out public.pem
# Encrypt with OAEP + SHA-256 (defaults to SHA-1 without the pkeyopt!)
openssl pkeyutl -encrypt -pubin -inkey public.pem \
-pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 \
-in plain.txt -out cipher.bin
# Decrypt
openssl pkeyutl -decrypt -inkey private.pem \
-pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 \
-in cipher.bin
# Convert PKCS#1 -> PKCS#8 if a library rejects your key
openssl pkcs8 -topk8 -nocrypt -in pkcs1.pem -out pkcs8.pem
// This is how TLS, PGP and every real system encrypt large payloads:
// RSA protects a random AES key; AES protects the data.
const aesKey = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const payload = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv }, aesKey, new TextEncoder().encode(bigDocument)
);
// Wrap the 32-byte AES key with RSA - well under the size limit.
const raw = await crypto.subtle.exportKey("raw", aesKey);
const wrapped = await crypto.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, raw);
// Send { wrapped, iv, payload }. The recipient unwraps with RSA,
// then decrypts the payload with AES.
When you need this
- Encrypting a short secret - an API key, a symmetric key, a one-time token - for a recipient whose public key you have.
- Testing that a public key you were given actually works before wiring it into a deployment.
- Producing a test ciphertext so a counterparty can verify their decryption path.
- Confirming whether a mismatch is caused by padding, by encoding, or by the wrong key.
Common problems and what causes them
- "Data too large for key size" / "message too long"
- RSA cannot encrypt data longer than its key. With OAEP and SHA-256 the limit is the modulus size minus 66 bytes - 2048-bit key: 190 bytes; 3072-bit key: 318 bytes; 4096-bit key: 446 bytes. If you need to encrypt more than that, you do not want plain RSA; see hybrid encryption below.
- Mixing OAEP and PKCS#1 v1.5 padding between the two sides
- A ciphertext padded with OAEP cannot be decrypted as PKCS#1 v1.5 or vice versa - you get a decryption error or garbage. Both sides must agree on the padding scheme and, for OAEP, on the hash function. OAEP with SHA-256 is the right default; PKCS#1 v1.5 encryption is vulnerable to Bleichenbacher-style attacks and should only be used for compatibility.
- PEM header mismatch: PKCS#1 vs PKCS#8 vs SPKI
- A key starting "-----BEGIN RSA PRIVATE KEY-----" is PKCS#1; "-----BEGIN PRIVATE KEY-----" is PKCS#8; "-----BEGIN PUBLIC KEY-----" is SPKI. Libraries are picky: Web Crypto wants PKCS#8 and SPKI, OpenSSL will read all of them, and Java wants PKCS#8. Converting is a one-line openssl command, not a reason to regenerate the key.
- Encrypting with the private key to 'sign'
- Signing is a separate operation with its own padding (RSASSA-PKCS1-v1_5 or RSASSA-PSS), not 'encryption with the private key'. Use the sign/verify APIs; do not invert encrypt/decrypt.
- Ciphertext encoding confusion
- RSA output is exactly as many bytes as the modulus (256 bytes for a 2048-bit key), and is then usually Base64'd for transport. If your Base64 decodes to a length that is not the modulus size, the ciphertext is truncated or double-encoded.
- Line endings and whitespace in a pasted PEM
- PEM parsers expect the base64 body wrapped at 64 characters with the exact header and footer lines. A PEM pasted from a JSON string with literal \n sequences, or with the newlines stripped, will fail to parse - convert the escapes back to real newlines first.
FAQ
- Why can't RSA encrypt my file or long string?
- RSA can only encrypt data smaller than its key. With OAEP and SHA-256 the maximum is the modulus size minus 66 bytes - 2048-bit key: 190 bytes; 3072-bit key: 318 bytes; 4096-bit key: 446 bytes. Real systems never encrypt bulk data with RSA: they generate a random AES key, encrypt the data with AES, and use RSA only to encrypt that key. That is called hybrid encryption and there is a worked example in the code section above.
- OAEP or PKCS#1 v1.5?
- OAEP with SHA-256 for anything new. PKCS#1 v1.5 encryption padding is vulnerable to Bleichenbacher-style chosen-ciphertext attacks and survives only for compatibility with old systems. Whichever you pick, both sides must match exactly, including the OAEP hash.
- How big should an RSA key be?
- 2048 bits is the practical minimum today and is fine for most purposes; 3072 or 4096 gives a longer margin. Note that RSA cost grows steeply - 4096-bit operations are several times slower than 2048 - and that modern systems increasingly prefer elliptic-curve keys (Ed25519, ECDSA P-256) for equivalent strength at far smaller sizes.
- What is the public exponent 65537 and should I change it?
- 65537 (0x10001) is the standard public exponent: large enough to avoid the small-exponent attacks that affect e=3, and cheap to compute because it has only two set bits. Leave it alone unless a specification tells you otherwise.
- Can I recover the public key from the private key?
- Yes - a private key contains everything needed, and `openssl rsa -in private.pem -pubout` extracts it. The reverse is not possible, which is the entire point.
- Is my private key safe to paste into this page?
- The operations run in your browser through the Web Crypto API and nothing is uploaded. That said, the safest habit with a production private key is to not paste it into any web page at all - generate a throwaway key here for testing, and keep real keys in your own tooling.
- What key format?
- SPKI PEM public key (BEGIN PUBLIC KEY).
Related reading
- PEM validator check a key's format
- AES-GCM encrypt for bulk data