Online RSA Encryption/Decryption: Asymmetric Crypto and Digital Signatures
What Is RSA Encryption
RSA is the most widely used asymmetric encryption algorithm, invented by Rivest, Shamir, and Adleman in 1977. It uses a key pair: public key for encryption, private key for decryption.
How RSA Works
Key Pair Generation:
Choose two large primes p, q
Compute n = p × q
Compute φ(n) = (p-1)(q-1)
Choose e such that gcd(e, φ(n)) = 1
Compute d such that e×d ≡ 1 (mod φ(n))
Public Key: (e, n) ← Can be shared
Private Key: (d, n) ← Must be kept secret
Encrypt: C = M^e mod n
Decrypt: M = C^d mod n
RSA vs AES
| Feature | RSA (Asymmetric) | AES (Symmetric) |
|---|---|---|
| Key count | Pair (public + private) | Single shared key |
| Speed | Slow (1000x slower) | Fast |
| Data size | Limited (< key length) | Unlimited |
| Typical use | Key exchange, signatures | Data encryption |
| Key length | 2048-4096 bits | 128-256 bits |
Why You Need RSA Encryption
- Secure communication: Key exchange in HTTPS/TLS handshakes
- Digital signatures: Verify data integrity and source
- API authentication: JWT RS256 signature verification
- Key exchange: Securely transmit AES session keys
- Code signing: Verify software publisher identity
How to Use an Online Tool
Using ToolVault's RSA Encryption/Decryption Tool:
- Generate RSA key pair (2048/4096 bits)
- Encrypt data with public key
- Decrypt data with private key
- Create and verify digital signatures
- Import/export in PEM format
RSA in Code
JavaScript: Web Crypto API
// Generate RSA key pair
async function generateRSAKeyPair() {
const keyPair = await crypto.subtle.generateKey(
{
name: 'RSA-OAEP',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256',
},
true,
['encrypt', 'decrypt']
);
return keyPair;
}
// Encrypt
async function rsaEncrypt(publicKey, data) {
const encoder = new TextEncoder();
const encrypted = await crypto.subtle.encrypt(
{ name: 'RSA-OAEP' },
publicKey,
encoder.encode(data)
);
return new Uint8Array(encrypted);
}
// Decrypt
async function rsaDecrypt(privateKey, encryptedData) {
const decrypted = await crypto.subtle.decrypt(
{ name: 'RSA-OAEP' },
privateKey,
encryptedData
);
return new TextDecoder().decode(decrypted);
}
Python: Using cryptography Library
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
# Generate key pair
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
public_key = private_key.public_key()
# Encrypt
ciphertext = public_key.encrypt(
b"Hello, RSA!",
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# Decrypt
plaintext = private_key.decrypt(
ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
Padding is not optional detail — it is the security
Textbook RSA (exponentiate the message, done) is deterministically insecure: the same plaintext always yields the same ciphertext, and short messages fall to direct root extraction. Every real deployment uses a padding scheme, and the choice is a compatibility minefield. PKCS#1 v1.5 is the legacy default that still works everywhere but is vulnerable to Bleichenbacher-style padding-oracle attacks when the decryptor leaks failure details. OAEP (OAEPwithSHA-256andMGF1) is the modern answer — but Java's default OAEP parameters and OpenSSL's differ in MGF1 digest, producing ciphertexts each side cannot open. Rule: encrypt with a library default, decrypt with a different stack, fail — check the OAEP parameter bytes before blaming the key.
Key sizes: how we got to 2048 (and why 4096 is usually wrong)
Factoring records pushed the floor: 512-bit fell in 1999, 768-bit in 2009, and 1024-bit is within reach of nation-state budgets. 2048-bit offers ~112 bits of security and satisfies every current CA and standard. Jumping to 4096 bits quadruples decryption cost for about 20 extra bits — instead, switch algorithms: for new hybrid designs, EC (P-256) or SM2 delivers 128-bit security at a fraction of the key size and compute. Use 4096 only where a counterparty standard freezes you into RSA.
The hybrid reality
RSA encrypts at most one block (190-245 bytes at 2048-bit, depending on padding) — nobody encrypts a document with RSA directly. Every real system is an envelope: generate a random AES/SM4 session key, encrypt the payload with it, encrypt the session key with RSA. TLS, PGP, and our hybrid tools all follow this shape. Debugging "RSA payload too long" errors usually means someone skipped the envelope step.
PEM formats: SPKI vs PKCS#8 vs PKCS#1
The Generate tab emits PEM text blocks, but PEM is just an envelope — the DER structure inside decides whether other software can open it:
| Header | Inner structure | Holds | Compatibility |
|---|---|---|---|
BEGIN PUBLIC KEY | SPKI (X.509 SubjectPublicKeyInfo) | Public key | Universal |
BEGIN RSA PUBLIC KEY | PKCS#1 RSAPublicKey | Public key | Legacy; Web Crypto rejects it |
BEGIN PRIVATE KEY | PKCS#8 PrivateKeyInfo | Private key | Universal |
BEGIN RSA PRIVATE KEY | PKCS#1 RSAPrivateKey | Private key | Legacy; Web Crypto rejects it |
BEGIN ENCRYPTED PRIVATE KEY | PKCS#8 wrapped with a passphrase | Encrypted private key | Decrypt first |
Conversion is a one-liner on the OpenSSL side: openssl pkcs8 -topk8 -nocrypt -in rsa.pem -out pkcs8.pem rewrites a PKCS#1 private key as PKCS#8. Keys exported from Java or Python as BEGIN PUBLIC KEY / BEGIN PRIVATE KEY paste straight into the tool — stray whitespace and CRLF line endings are stripped on import.
Troubleshooting common failures
"DataError" on decrypt (browser): the ciphertext was not produced by the matching public key, or it got truncated or altered. A single lost base64 character triggers this — check your copy/paste first.
Plaintext too long: with OAEP/SHA-256 the single-block limit is key bytes − 66. 2048-bit holds 190 bytes of UTF-8 (~63 CJK characters); 4096-bit holds 446. The tool's Encrypt tab now shows a live byte counter against the limit and disables the button past it — that is the hint to switch to hybrid encryption, not a bug.
"BEGIN RSA PRIVATE KEY" won't import: that is a PKCS#1 private key; Web Crypto only accepts PKCS#8 (BEGIN PRIVATE KEY). Convert with openssl pkcs8 -topk8 -nocrypt -in rsa.pem -out pkcs8.pem.
Cross-language mismatch (Java encrypts, browser can't decrypt): nine times out of ten the OAEP parameters differ. Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding still defaults MGF1 to SHA-1, while Web Crypto's hash: 'SHA-256' locks MGF1 to SHA-256 as well. On the Java side, pass new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT) explicitly.
FAQ
How to Choose RSA Key Length?
- 2048 bits: Current standard, sufficiently secure
- 3072 bits: Higher security requirements
- 4096 bits: Maximum security level (lower performance) NIST recommends at least 2048 bits, 3072+ bits after 2030.
RSA vs ECC — Which Is Better?
ECC (elliptic curve) has shorter keys for the same security level (256-bit ECC ≈ 3072-bit RSA) and better performance. But RSA is more mature with better compatibility. New projects recommend ECC; existing systems keep RSA.
Why Can't RSA Directly Encrypt Large Files?
RSA has length limits (key length - padding overhead) and is slow. In practice, RSA encrypts AES keys, then AES encrypts the file (hybrid encryption). You can handle the symmetric AES side with our AES Encrypt/Decrypt tool.
Can I paste a PEM key with line breaks into the tool?
Yes. The 64-character line wrapping is a display convention; import strips all whitespace before base64-decoding. But check the header line: the public key must say BEGIN PUBLIC KEY and the private key BEGIN PRIVATE KEY — variants with RSA in the header are legacy PKCS#1, which Web Crypto does not support.
How many CJK characters fit in one 2048-bit RSA block?
The OAEP/SHA-256 limit is 190 bytes, about 63 CJK characters in UTF-8. The Encrypt tab shows a live "bytes / limit" counter and disables the button past the limit. For longer content, the right move is hybrid encryption, not a larger RSA key.
Triage notes
The error list is arranged by interop scenario: the cross-language mismatch gets an entry of its own with the assertion that nine times out of ten it is OAEP parameter drift — Java's OAEPWithSHA-256AndMGF1Padding still defaults MGF1 to SHA-1, while Web Crypto locks MGF1 to SHA-256 as well, and one digest field of difference makes the ciphertexts mutually unopenable. The PEM table distinguishes SPKI/PKCS#8 from legacy PKCS#1 by the header line, because Web Crypto will not accept the RSA-bearing headers — the most recognizable class of import failure. The annotation on 'payload too long' runs on the same logic: eight times out of ten someone skipped the envelope step, and the correct move is hybrid encryption, not a bigger key.
This field note comes from ToolVault. Browse more online tools on the homepage.
Related Tools
Related Articles
Post-Quantum Migration Self-Check: From Algorithm Inventory to Hybrid TLS — 12 Things to Do in 2026
A practical self-check framework for PQC migration: why three overlapping regulatory timelines make 2026 the starting line, how to build a cryptographic bill of materials (CBOM), ranking by HNDL exposure, enabling X25519+ML-KEM hybrid TLS, and what crypto-agility means in practice. With a 12-item checklist.
Passkeys, Explained: Why the Server Can No Longer Hold a Copy of Your Password
Passkeys (WebAuthn/FIDO2) deepened: registration vs assertion ceremonies, platform authenticators and roaming keys, synced passkeys, how they relate to passwords/TOTP/JWT, and why origin binding makes phishing resistance structural. Servers store public keys only; note that this site has no dedicated Passkey tool page.
ML-KEM, Explained: How Kyber Got Into Your TLS Handshake
An engineering tour of ML-KEM (FIPS 203, formerly Kyber): Kyber-to-standard naming, encapsulate/decapsulate and IND-CCA, the three parameter sizes, how X25519+ML-KEM feeds the TLS 1.3 HKDF schedule, the ClientHello-bloat middlebox trap, OpenSSL 3.5 / nginx enablement, and browser-side implementation boundaries.