The key distribution problem
Symmetric cryptography has a bootstrapping problem: to encrypt securely, both sides need the same secret key — but how do you get that key to the other side without an eavesdropper intercepting it first?
Public-key (asymmetric) cryptography, introduced conceptually by Diffie and Hellman in 1976 and made practical by Rivest, Shamir, and Adleman with RSA in 1977, solves this with two mathematically linked keys: a public key anyone can know, and a private key only the owner holds. Data encrypted with the public key can only be decrypted with the matching private key.
How RSA works, at a glance
RSA's security rests on a simple asymmetry: multiplying two large prime numbers is easy, but factoring their product back into the original primes is computationally hard for classical computers when the numbers are large enough (2048 bits or more, today).
Key generation picks two large random primes p and q, computes n = p × q, and derives a public exponent e and private exponent d such that they're mathematically inverse under modular arithmetic tied to n. The public key is (n, e); the private key is (n, d). Encryption raises the message to the power e mod n; decryption raises the ciphertext to the power d mod n.
In practice, RSA is rarely used to encrypt bulk data directly — it's slow and has strict size limits. Instead it's typically used to encrypt a short symmetric key (key transport) or to produce digital signatures, with AES doing the heavy lifting on the actual data.
RSA key generation, encryption, and decryption
- 1
Generate keys
Pick large random primes p, q. Compute n = pq and φ(n) = (p−1)(q−1). Choose e coprime to φ(n), then derive d as e's inverse mod φ(n).
- 2
Publish the public key
(n, e) is shared openly. (n, d) — the private key — never leaves the owner.
- 3
Encrypt
Anyone computes C = Mᵉ mod n using only the public key.
- 4
Decrypt
Only the private-key holder computes M = Cᵈ mod n to recover the message.
n is the modulus; φ(n) (Euler's totient) is only computable if you know p and q.
The public exponent e and private exponent d are chosen to be modular inverses of each other.
Encryption with the public key (n, e); decryption with the private key (n, d).
A worked example with small numbers
Real RSA uses primes hundreds of digits long, but the same arithmetic works identically with small ones — which is why this toy example (the one most textbooks use) is worth stepping through by hand.
Take p = 61 and q = 53. Then n = 3233 and φ(n) = 60 × 52 = 3120. Pick e = 17 (it shares no common factors with 3120). Solving 17d ≡ 1 (mod 3120) gives d = 2753 — that's the whole key pair: public key (3233, 17), private key (3233, 2753).
Encrypting the message M = 65 gives C = 65¹⁷ mod 3233 = 2790. Decrypting runs it back: 2790²⁷⁵³ mod 3233 = 65. The same modulus, two different exponents, one direction easy without the private key and the other only possible with it.
Finding d: the extended Euclidean algorithm
Solving "17d ≡ 1 (mod 3120)" isn't guesswork — it's a standard computation called the extended Euclidean algorithm, which finds the modular inverse of e directly. Run backward, the ordinary Euclidean algorithm (repeated division to find a greatest common divisor) leaves behind a trail of remainders; the extended version tracks coefficients alongside those remainders and, because e and φ(n) were chosen to be coprime, that trail terminates in exactly the d you need. It's fast — logarithmic in the size of the numbers — which is what makes key generation practical even for 2048-bit primes.
Why e = 65537 shows up everywhere
Almost every RSA key you'll encounter uses the public exponent e = 65537, and the reason is a genuine engineering trade-off rather than convention. Written in binary, 65537 is 10000000000000001 — only two bits are set — and the square-and-multiply algorithm used for modular exponentiation does one multiplication per bit and one extra squaring per set bit, so a sparse exponent like this makes encryption and signature verification (both of which use e) noticeably faster than a dense one would.
Smaller exponents like e = 3 are even faster, but they reopen exactly the kind of low-exponent attacks covered below — 65537 is small enough to stay fast and large enough to close off the simplest ones. It also happens to be a Fermat prime (2¹⁶ + 1), which guarantees it's coprime with φ(n) for essentially any RSA modulus, simplifying key generation.
RSA signatures: the same math, opposite roles
Encryption and signing use identical RSA arithmetic with the public and private key's roles reversed. To encrypt, anyone raises a message to the public exponent; to sign, the key-holder raises a (hashed) message to their private exponent — something only they can do. To decrypt, the key-holder raises the ciphertext to their private exponent; to verify a signature, anyone raises it to the public exponent and checks the result matches the message's hash.
In practice, following the hash-then-sign pattern from the hashing module, RSA never signs a raw message directly — it signs a padded hash digest (via RSA-PSS, covered in the padding module), for exactly the same reasons raw RSA encryption is unsafe.
Why n has to be exactly two large primes — not one, not thirty
The requirement "n = p × q for two large primes" is doing more work than it looks like. Both directions of getting it wrong break RSA completely, for different reasons.
Using a single prime as the modulus (n = p, with no q at all) breaks it immediately: φ(n) for a prime is just n − 1, computable on the spot with no factoring whatsoever, since the modulus's primality is already public. An attacker skips straight to finding d — the entire security argument evaporates, because "hard to factor n" was never a barrier to begin with.
Using many small primes multiplied together (n = p₁ × p₂ × ⋯ × p₃₀, say) breaks it the opposite way: n is trivially factorable by algorithms tuned for exactly this shape, like Pollard's rho or the elliptic curve method, which are fast whenever n has small factors — even if n itself is a large number. RSA's security specifically depends on n having no small factors at all, which is precisely what two similarly-sized large primes guarantees and a product of many small ones doesn't.
Practice
A broken RSA setup mistakenly uses n = 1000000007 directly as the modulus — n itself is prime, with no second factor. What is φ(n)?
Practice #2
A different broken setup uses n = 510510, the product of the seven small primes 2 × 3 × 5 × 7 × 11 × 13 × 17. What is φ(n)?
What can go wrong: classic implementation attacks
RSA's math is sound; most real-world breaks come from how it's deployed. Three patterns recur across decades of RSA vulnerabilities, and none of them require factoring anything.
RSA pitfalls that have nothing to do with factoring
Common modulus attack
If two users are (incorrectly) issued the same n with different e values, anyone who intercepts the same message encrypted to both can recover it algebraically — without ever factoring n.
Håstad's broadcast attack
The same message sent to several recipients using a small e (like 3) and different moduli can be recovered using the Chinese Remainder Theorem, entirely bypassing the private keys.
Weak randomness in key generation
If the "random" primes p and q aren't actually independent and unpredictable, keys across different devices can end up sharing a prime factor — a real issue found in some embedded devices' RSA key generation.
Why factoring is the whole game
Every attack on RSA either tries to factor n directly or tries to find a shortcut that avoids factoring. The best known classical factoring algorithm, the General Number Field Sieve (GNFS), has sub-exponential running time — hard enough that factoring a 2048-bit RSA modulus is considered infeasible with any classical computer for the foreseeable future.
This is precisely the assumption that Shor's algorithm breaks on a sufficiently large quantum computer — see the quantum threat module for why RSA is on every PQC migration roadmap.
The factoring record, over time
RSA-100 (330 bits)
Factored in 1991.
RSA-129 (426 bits)
The modulus from the original 1977 RSA challenge — factored in 1994, using idle computer time volunteered over the internet.
RSA-768 (768 bits)
Factored in 2009, after roughly two years of computation across many machines.
RSA-2048 (2048 bits)
Today's minimum recommended size — still unfactored, and expected to stay that way classically for the foreseeable future.
GNFS's running time — sub-exponential, but still growing fast enough that doubling n's bit length costs far more than double the effort.
Go deeperTrial division: factoring a modulus that's simply too small⌄
Before reaching for anything clever, the most basic factoring approach is worth doing by hand once: test each small prime as a possible divisor of n, in order, until one divides evenly. This is trial division, and it's genuinely how you'd factor any RSA modulus small enough that its smaller prime factor is itself only a few digits — which is exactly why real RSA moduli use primes hundreds of digits long, putting trial division's O(√n) running time completely out of reach.
Worked example: n = 979. Testing 2, 3, 5, 7 all fail; 11 divides evenly (979 ÷ 11 = 89), and 89 is itself prime. So n = 11 × 89 — found in five quick divisions, because the smaller factor happened to be tiny.
Practice
Factor n = 1313 using trial division (test 2, 3, 5, 7, 11, 13, ... in order). Give the smaller prime factor.
Go deeperThe cube-root attack: RSA with no padding and a small exponent⌄
If a message is encrypted with e = 3 and never padded, and the message M is small enough that M³ is actually less than the modulus n, something breaks completely: the modular reduction never triggers. C = M³ mod n is just C = M³, an ordinary integer cube — and anyone can recover M by taking an integer cube root, without touching the private key or factoring anything.
Worked example: M = 123, e = 3, and n large enough that M³ < n. Then C = 123³ = 1,860,867. Taking the integer cube root of 1,860,867 gives back 123 directly. This is exactly why the padding module's warning about raw RSA matters in practice, not just in theory — OAEP defeats this attack by padding M out to the full size of n before encrypting, so M³ is always far larger than n and the modular wraparound always happens.
Practice
A message M was encrypted with e = 3 and no padding, small enough that no modular reduction occurred. The ciphertext is C = 10218313. Recover M.
Go deepere = 1: the exponent that makes the modulus irrelevant⌄
Take the small-exponent problem to its extreme and something even more degenerate happens. With e = 1, encryption is C = M¹ mod n = M mod n — and since a message M is always chosen smaller than n to begin with, M mod n is just M. The modulus n never does anything at all; the "ciphertext" is the plaintext, unchanged, in plain sight.
No cube root, no factoring, not even an integer root extraction — recovering M from C requires nothing but reading it. This is an extreme case of the same lesson as the cube-root attack: an exponent chosen for "efficiency" without any regard for the resulting math can throw away the encryption entirely, not just weaken it.
Practice
An RSA public key uses e = 1. The published ciphertext is C = 424242. What is the plaintext M?
Go deeperFermat's factorization: when p and q are too close together⌄
RSA's security assumes p and q are independently random primes of similar bit length — but if a flawed key generator picks them too close to each other, n = p×q can be factored almost instantly, no GNFS required. The trick: if p and q are close, then n = a² − b² for a = (p+q)/2 and b = (p−q)/2, and a ≈ √n. Starting from a = ⌈√n⌉ and incrementing, check at each step whether a² − n is a perfect square — the first one that is gives b, and then p = a−b, q = a+b.
Worked example: p = 10007, q = 10009 (deliberately close). n = p×q = 100,160,063. ⌈√n⌉ = 10008. Compute 10008² − n = 100,160,064 − 100,160,063 = 1, which is 1² — a perfect square on the very first try. So b = 1, giving p = 10008−1 = 10007 and q = 10008+1 = 10009, recovered in a single step instead of factoring n the hard way.
Practice
n = 10002200057 was generated from two primes chosen too close together. Using Fermat's method (a = ⌈√n⌉, checking a²−n for a perfect square), find the smaller prime factor.
Go deeperWiener's attack: why the private exponent can't be small⌄
Just as e is usually chosen small for fast encryption, it might seem tempting to choose a small d for fast decryption. Wiener's attack shows exactly why that's disastrous: when d is smaller than roughly n^0.25, the fraction e/n turns out to be a very close rational approximation of a related fraction involving d — close enough that the continued-fraction expansion of e/n reveals d directly, with no factoring and no brute force at all.
This is the mirror image of the e = 65537 story from earlier in this module: e gets to be small because encryption speed only matters to the sender, but d must always be large, because decryption speed advantages for the key-holder aren't worth handing an attacker a shortcut. Real implementations enforce a minimum size for d specifically to stay outside Wiener's reach.
Practice
n is a 2048-bit RSA modulus. Wiener's attack succeeds once d drops below roughly n^0.25. About how many bits is that threshold?