RSA
RSA basics: public-key encryption + signatures. Defensive use only: prefer modern alternatives where you can, and use vetted libraries everywhere.
Crypto — RSA
EXAMPLE
# ===== What RSA is =====
# Asymmetric algorithm: each user has a (public key, private key) pair.
# Encrypt with public; decrypt with private.
# Sign with private; verify with public.
# Based on the difficulty of factoring large semiprime integers.
# ===== Where RSA appears =====
# - TLS certificates (X.509, often RSA 2048+)
# - SSH keys (legacy; ED25519 is preferred)
# - JWTs signed with RS256
# - Email (S/MIME, PGP)
# - Signed software updates
# ===== Practical sizes =====
# 2048-bit minimum for production
# 3072-bit if you want extra margin
# 4096-bit common for long-lived signing keys
# < 2048-bit is broken / weak; do not generate new < 2048-bit keys
# ===== Generate keys (OpenSSL) =====
openssl genpkey -algorithm RSA -out priv.pem -pkeyopt rsa_keygen_bits:3072
openssl rsa -in priv.pem -pubout -out pub.pem
# View:
openssl rsa -in priv.pem -text -noout
# ===== Sign + verify (OpenSSL) =====
openssl dgst -sha256 -sign priv.pem -out sig.bin message.txt
openssl dgst -sha256 -verify pub.pem -signature sig.bin message.txt
# ===== Node example =====
import { generateKeyPair, sign, verify, createPrivateKey, createPublicKey } from 'node:crypto';
const { publicKey, privateKey } = await new Promise((resolve, reject) =>
generateKeyPair('rsa', { modulusLength: 3072 }, (err, pub, priv) =>
err ? reject(err) : resolve({ publicKey: pub, privateKey: priv })));
const message = Buffer.from('hello');
const signature = sign('sha256', message, privateKey); // RSA-PKCS1
const ok = verify('sha256', message, publicKey, signature); // true
# For RSA-PSS (preferred over PKCS1 v1.5 for new code):
const sigPss = sign('sha256', message, { key: privateKey, padding: 6 /* RSA_PKCS1_PSS_PADDING */ });
# Encrypt small payloads with OAEP:
import { publicEncrypt, privateDecrypt, constants } from 'node:crypto';
const ct = publicEncrypt({ key: publicKey, padding: constants.RSA_PKCS1_OAEP_PADDING, oaepHash: 'sha256' }, message);
const pt = privateDecrypt({ key: privateKey, padding: constants.RSA_PKCS1_OAEP_PADDING, oaepHash: 'sha256' }, ct);
# Python (cryptography):
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization
key = rsa.generate_private_key(public_exponent=65537, key_size=3072)
pub = key.public_key()
ct = pub.encrypt(b'hi', padding.OAEP(mgf=padding.MGF1(hashes.SHA256()), algorithm=hashes.SHA256(), label=None))
pt = key.decrypt(ct, padding.OAEP(mgf=padding.MGF1(hashes.SHA256()), algorithm=hashes.SHA256(), label=None))
# ===== Common gotchas =====
# - RSA can only encrypt small payloads (smaller than key size minus padding).
# For larger: use hybrid encryption — RSA encrypts a fresh AES key; AES encrypts data.
# - Always pair RSA with PADDING (OAEP for encryption; PSS for signatures).
# - PKCS1 v1.5 padding is OK for legacy compatibility but use OAEP/PSS for new code.
# - Never roll your own RSA implementation.
# ===== Modern alternatives =====
# Ed25519 fast, small keys (32 bytes), preferred for signatures
# X25519 fast key exchange
# ECDSA P-256 widely supported in TLS / JWTs (ES256)
# RSA still ubiquitous for TLS + legacy systems; prefer Ed25519/EdDSA for new keys.
# ===== Key rotation + storage =====
# - Store private keys in HSM / KMS (AWS KMS, GCP KMS, HashiCorp Vault)
# - Rotate signing keys yearly; long-lived TLS certs follow CA cadence
# - Document recovery; lost key = lost identity / data
# ===== Patterns to internalise =====
# - 3072-bit RSA minimum for new keys; or switch to Ed25519
# - OAEP for encryption padding; PSS for signing
# - Hybrid (RSA + AEAD) for any payload over a few hundred bytes
# - Rotate + store keys in a KMS
# ===== Pitfalls =====
# - Using textbook / unpadded RSA -> chosen ciphertext attacks
# - PKCS1 v1.5 with new code -> use PSS / OAEP
# - Comparing signatures with == instead of timingSafeEqual
# - Storing private keys in environment variables or plain files
Why it matters
RSA is the workhorse of public-key crypto in TLS, JWTs, and legacy systems. Use vetted libraries, 3072-bit keys, OAEP for encryption, PSS for signatures, hybrid encryption for anything non-tiny. For new keys, EdDSA / Ed25519 is faster and smaller — reach for it when compatibility allows.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// 2048 bits MINIMUM. Always use OAEP for encryption and PSS for signatures —
// never PKCS#1 v1.5 in new code.
const { generateKeyPairSync, publicEncrypt } = require('crypto');
const { publicKey } = generateKeyPairSync('rsa', { modulusLength: 3072 });
Try it Yourself »
Discussion
Loading…