Generate RSA key pairs in the browser, encrypt and decrypt with RSA-OAEP, sign with RSA-PSS or PKCS#1 v1.5, and convert keys between SPKI, PKCS#8 and PKCS#1 PEM formats. Everything runs locally with the WebCrypto engine — no key or message ever leaves this page.
What RSA is for: small messages and signatures, not bulk data. RSA encrypts at most k−2·hashLen−2 bytes per call (190 bytes for a 2048-bit key with SHA-256) and is ~1000× slower than AES. Real systems use hybrid encryption: RSA wraps a random AES key, AES encrypts the data (see the AES tool). This page exists for learning, testing and interop checks.
1. Generate a key pair (WebCrypto, local; the four PEM formats are produced together)
Modulus size:
Public key — SPKI (-----BEGIN PUBLIC KEY-----, the usual "RSA public key" format)
Public key — PKCS#1 (-----BEGIN RSA PUBLIC KEY-----, bare modulus + exponent)
Private key — PKCS#8 (-----BEGIN PRIVATE KEY-----, unencrypted; treat like a password)
Generating a key in the browser does not encrypt it. For storage, move it into a real key vault or encrypt it offline.
2. Or import an existing key (paste any of the four PEM formats)
3. Encrypt / decrypt (RSA-OAEP) - direction follows the current key: public = encrypt, private = decrypt
Hash:Label:
PKCS#1 v1.5 encryption is deliberately not implemented: it has no ciphertext integrity and falls to padding-oracle attacks (Bleichenbacher). OAEP is the only encryption mode a new design should use.
4. Sign (requires the private key) - PSS (randomized, modern) or PKCS#1 v1.5 (deterministic, legacy-compatible)
Scheme:Hash:
5. Verify (requires the public key) - paste the message and the signature
Scheme:Hash:
How much can one RSA call carry? OAEP message limit = key bytes − 2×hash bytes − 2:
Modulus
SHA-256
SHA-384
SHA-512
1024-bit
62 bytes
30 bytes
0 bytes
2048-bit
190 bytes
126 bytes
62 bytes
3072-bit
318 bytes
254 bytes
190 bytes
4096-bit
446 bytes
382 bytes
318 bytes
Signatures have no message limit — the message is hashed first; the signature itself is always exactly the key size (256 bytes for 2048-bit).
Key strength and lifetime (NIST SP 800-57 Part 1 Rev. 5): 1024-bit was disallowed for new use after 2013; 2048-bit is the minimum today and remains acceptable through 2030; 3072-bit and beyond are the comfortable choice for 2031+. 8192-bit works here but generation can take a minute or more in the browser and buys security nobody currently needs — prefer 3072/4096.
OpenSSL interop — the PEMs this page produces round-trip on the command line: openssl pkeyutl -encrypt -pubin -inkey pub.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 openssl pkeyutl -decrypt -inkey priv.pem -pkeyopt rsa_padding_mode:oaep ... openssl dgst -sha256 -sign priv.pem -out msg.sig msg.txt (PKCS#1 v1.5) / openssl dgst -sha256 -verify pub.pem -signature msg.sig msg.txt
PSS: add -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:32 to both dgst calls. Key format conversion: openssl rsa -in key.pem -RSAPublicKey -out pubkey.pem does SPKI ↔ PKCS#1 on disk; this page does it in the browser.
Self-test
Runs the page engine against independent anchors: key generation at three modulus sizes, OAEP encrypt/decrypt roundtrips, PSS and PKCS#1 v1.5 sign/verify across hashes, a deterministic v1.5 signature vector byte-for-byte (generated by an independent native implementation), a fixed OAEP ciphertext and PSS signature produced by OpenSSL that this page must decrypt/verify (cross-implementation interop), PEM format conversion roundtrips, and negative tests (tampered signature, wrong key must fail). Nothing is sent anywhere.
RSA encryption and signatures explained from scratch
In one sentence: RSA is public-key cryptography — anyone can encrypt to you with the public key or verify your signatures, but only the private key can decrypt or sign — and its security rests on the fact that factoring a 2048-bit number into two primes is beyond any computer that exists.
What RSA actually computes
Two primes and a trapdoor. An RSA key starts as two large random primes p and q; their product n (the modulus, hence "2048-bit key") is published in the public key together with an exponent e (almost always 65537). The private key is the pair d, the modular inverse of e − computed from p and q — plus shortcuts derived from the primes that speed private-key operations up 4×.
Encryption and signing are the same exponentiation. Encrypting a message block m means computing me mod n; decrypting is cd mod n. Signing a pre-formatted block with the private exponent and verifying with the public one is the same operation in the other direction. Everything else on this page — OAEP, PSS, PKCS#1 — is about formatting that block safely so raw exponentiation is never exposed.
Why it is slow. One RSA operation on a 2048-bit key costs on the order of a million times more CPU than encrypting one block with AES. That is why TLS uses RSA (or ECC) only to agree on a random AES key, then switches. The same rule applies to anything you build: RSA for the key, AES for the data.
Key size. The modulus length is the security dial: 1024-bit is broken-by-policy (factoring is near the reach of nation-states), 2048-bit ≈ 112-bit symmetric strength, 3072-bit ≈ 128-bit, 15360-bit would match a 256-bit symmetric key. Post-quantum note: Shor’s algorithm breaks RSA outright on a large quantum computer — one more reason to keep key lifetimes finite.
OAEP: the only encryption padding you should use
The problem with raw RSA. Textbook RSA (me mod n on the raw message) is deterministic and malleable: identical messages encrypt identically, an attacker can multiply a ciphertext by the encryption of 2 to make the plaintext double, and short messages fall to meet-in-the-middle. It must never be used directly.
What OAEP does. OAEP (PKCS#1 v2, RFC 8017) mixes the message with a random seed through a hash-based Feistel network before exponentiation. The result: encryption is randomized (the same plaintext gives a different ciphertext every time — run the OAEP Sample twice and compare), tampering is detected on decryption, and the message must fit inside the modulus with room for the hash — the k−2·hashLen−2 limit tabulated above.
The label. An optional public string bound into the padding. Both sides must supply the identical label or decryption fails — like the AAD in AES-GCM, it lets you bind ciphertext to context (a file name, an algorithm id). Empty is normal.
PKCS#1 v1.5 encryption is absent on purpose. The 1993-era padding has no integrity and leaks information through decryption error behaviour (Bleichenbacher’s 1998 attack turns a padding oracle into full decryption with a few million crafted ciphertexts; it still breaks real systems as ROBOT, 2017). WebCrypto refuses to implement it; so does this page.
PSS vs PKCS#1 v1.5 signatures
Sign = hash, then pad, then exponentiate. Both schemes hash the message (SHA-256/384/512 here), format the digest into a block, and apply the private exponent. Verify re-formats the block with the public key and compares. The difference is the formatting:
Scheme
Block contents
Properties
RSASSA-PKCS1-v1_5
digest + digest-info header, deterministic
Every signature of the same message is byte-identical (check the self-test: its v1.5 vector is fixed). Universal legacy support: X.509 certificates, JWT RS*, TLS before 1.3. No known forge when verification is implemented correctly.
RSA-PSS
digest + random salt of hash length, MGF1 masking
Randomized — each signature differs. Provably secure in the random-oracle model; required in TLS 1.3 certificates-of-the-future and newer protocols. Verifier must know the salt length (this page uses hash length, the common default).
Which to pick. New designs: PSS. Interop with existing certificates, JWTs (RS256) or old peers: v1.5. Both are safe as signature schemes — the v1.5 disaster story is about *encryption* padding, a different construction despite the similar name.
PKCS#1 vs PKCS#8 vs SPKI: why the same key has four PEMs
PEM is just base64-wrapped DER. A PEM file is a label plus base64 of a DER (binary ASN.1) structure. The label tells you which structure — and that is the whole difference between the four formats this page generates:
PEM label
DER structure
Contains
-----BEGIN PUBLIC KEY-----
SubjectPublicKeyInfo (SPKI)
Algorithm OID + public key (n, e). The default "public key" everywhere.
-----BEGIN RSA PUBLIC KEY-----
PKCS#1 RSAPublicKey
Bare (n, e). Old OpenSSL and some embedded stacks.
-----BEGIN PRIVATE KEY-----
PKCS#8 PrivateKeyInfo
Algorithm OID + private key. Algorithm-agnostic container, the modern default.
-----BEGIN RSA PRIVATE KEY-----
PKCS#1 RSAPrivateKey
n, e, d, p, q, …. The classic OpenSSL genrsa output.
Same key, different wrappers. All four boxes above after Generate hold the same key pair — SPKI and PKCS#1 public contain identical n, e; PKCS#8 and PKCS#1 private contain identical private values. This page converts between them losslessly in the browser (its self-test round-trips the conversions). Confusing the formats is the single most common cause of "invalid key" errors when pasting keys between tools.
Encrypted PEMs (-----BEGIN ENCRYPTED PRIVATE KEY-----) are PKCS#8 with a password-based encryption layer. This page does not accept them — decrypt them offline (openssl pkcs8 -in enc.pem -out plain.pem) first.
Common mistakes
Encrypting bulk data with RSA. Beyond the hard OAEP size limit it is absurdly slow. The pattern is: generate a random AES key, RSA-OAEP-encrypt the AES key, AES-GCM-encrypt the data, send both parts.
Shipping the private key because "it’s a test". The sample keys embedded in this page’s vectors are published deliberately — they can only ever sign test data. Your keys are not like that.
Verifying with the private key. It usually "works" mathematically and some tools allow it, but distributing a private key to verifiers defeats the whole model. Verify with the public key (this page enforces it).
Comparing signatures byte-for-byte to check code. PSS (and OAEP) are randomized: two correct runs produce different outputs. Compare by verifying, not by equality — only v1.5 signatures are deterministic.
1024-bit keys "because they’re faster". They are also within reach of well-funded factoring. The speed argument died with hybrid encryption: RSA runs once per session, not per byte.
Hardcoding a salt length other than hash length for PSS. The verifier must agree; hash length (this page’s choice and OpenSSL’s -sigopt rsa_pss_saltlen:32) is the interoperable default.
FAQ
Why did my decryption fail with "wrong key, wrong hash or corrupted ciphertext"? OAEP decryption fails when any of: it is not the matching private key, the hash differs from the encryption side, the label differs, or a ciphertext byte changed. The error is deliberately identical for all cases — revealing which check failed is itself a padding-oracle leak.
Can I get the public key from the private key? Yes — the private formats contain n and e too. This page shows both after Generate; OpenSSL does it with openssl pkey -in priv.pem -pubout.
Why 65537 as the exponent? It is prime, has only two 1-bits (fast exponentiation), and is large enough to avoid the low-exponent attacks that hit e=3. It has been the standard choice since the 1990s; a "weird" exponent usually means a weak or non-standard key.
Is RSA dead because of quantum computers? Not yet — no machine exists that can run Shor’s algorithm on a 2048-bit modulus. But data that must stay secret into the 2030s+ is already being migrated; PQC hybrid schemes are the engineering answer.
What about openssl rsautl? It is the deprecated ancestor of pkeyutl. It defaults to v1.5 encryption padding — one more reason it was retired.