UUID Studio

RSA Decrypt

RSA-OAEP decryption with your private key.

  • 🔒 No data stored or uploaded
  • âš¡ 100% client-side
  • 🆓 Free, no account

Need more than one tool at a time? Open the full Workbench - or press Ctrl+K to jump to any tool.

Hash, HMAC, AES-GCM/CBC + RSA-OAEP, codecs, JWT decoding, UUID v4, and secure random - all client-side. JWTs use Base64URL (three segments), not a single MIME Base64 block - use Decode JWT below, not raw Base64 decode.

About RSA Decrypt

RSA decryption uses the private key to recover data that was encrypted with the matching public key. Only the private key holder can do it, and there is no way to derive the private key from the public one.

Decryption fails loudly and unhelpfully when anything does not line up, which is why most visits to a page like this are diagnostic. The usual culprits, in order: the padding scheme differs between the two sides, the OAEP hash differs (OpenSSL historically defaults to SHA-1 while most libraries default to SHA-256), the ciphertext was mangled in transport, or the private key does not match the public key that was used.

A valid RSA ciphertext is always exactly as long as the modulus - 256 bytes for a 2048-bit key, 512 for 4096. If your Base64 decodes to any other length, the problem is the transport encoding rather than the key.

Do not treat a padding failure as a normal branch in production code. Distinguishable padding errors are precisely what chosen-ciphertext attacks exploit, so fail with a single generic error rather than reporting why.

Decryption here runs entirely in your browser through the Web Crypto API. Even so, prefer a test key over a production private key in any web page.

How to use the RSA Decrypt

  1. Paste your RSA private key in PEM form. Web Crypto needs PKCS#8 (-----BEGIN PRIVATE KEY-----); convert a PKCS#1 key with openssl pkcs8 -topk8 -nocrypt.
  2. Paste the Base64 ciphertext. Check that it decodes to exactly the modulus length for your key.
  3. Select the same padding and hash the sender used - OAEP with SHA-256 is the common default.
  4. Decrypt. If it fails, change one variable at a time: first the OAEP hash, then the padding scheme, then confirm the key pair matches.

RSA Decrypt in code

The same operation this tool performs, in the languages you are most likely to need it.

JavaScript (browser / Web Crypto)
// 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);
Node.js
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());
Python (cryptography)
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(),
)
OpenSSL
# 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
Hybrid encryption (for data of any size)
// 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

  • Recovering a short secret sent to you encrypted under your public key.
  • Debugging why a partner's ciphertext will not decrypt in your application.
  • Confirming a private key really is the counterpart of a given public key.
  • Checking whether a stored ciphertext is still decryptable after a key rotation.

Common problems and what causes them

Decryption fails with no useful error
Work through it in this order: (1) OAEP hash mismatch - OpenSSL's default is SHA-1, most libraries use SHA-256; (2) padding scheme mismatch between OAEP and PKCS#1 v1.5; (3) ciphertext corrupted or re-encoded in transit; (4) the private key is not the pair of the public key used to encrypt.
"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 does decryption fail when I have the right private key?
Most often the OAEP hash differs between the encrypting and decrypting side. OpenSSL's pkeyutl defaults to SHA-1 unless you pass -pkeyopt rsa_oaep_md:sha256, while Web Crypto, Node and Python all commonly use SHA-256. After that, check the padding scheme and whether the ciphertext was re-encoded in transport.
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.
Key format?
PKCS#8 PEM private key (BEGIN PRIVATE KEY).

Related reading