← All modules
Public-key·30 min

RSA & public-key cryptography

Two mathematically linked keys — one public, one private — solve the problem symmetric crypto can't: how do you share a secret with someone you've never met?

Developer / EngineerSecurity ArchitectExecutive / Business LeaderResearcher / AcademicCurious Explorer

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. 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. 2

    Publish the public key

    (n, e) is shared openly. (n, d) — the private key — never leaves the owner.

  3. 3

    Encrypt

    Anyone computes C = Mᵉ mod n using only the public key.

  4. 4

    Decrypt

    Only the private-key holder computes M = Cᵈ mod n to recover the message.

n=p×q,φ(n)=(p1)(q1)n = p \times q, \qquad \varphi(n) = (p-1)(q-1)

n is the modulus; φ(n) (Euler's totient) is only computable if you know p and q.

ed1(modφ(n))e \cdot d \equiv 1 \pmod{\varphi(n)}

The public exponent e and private exponent d are chosen to be modular inverses of each other.

C=MemodnM=CdmodnC = M^{e} \bmod n \qquad\Longrightarrow\qquad M = C^{d} \bmod n

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.

p=61, q=53    n=3233, φ(n)=3120, e=17, d=2753p=61,\ q=53 \;\Rightarrow\; n=3233,\ \varphi(n)=3120,\ e=17,\ d=2753
C=6517mod3233=2790M=27902753mod3233=65C = 65^{17} \bmod 3233 = 2790 \qquad M = 2790^{2753} \bmod 3233 = 65

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.

e=65537=216+1e = 65537 = 2^{16} + 1

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.

Sign: S=H(m)dmodnVerify: H(m)=?Semodn\text{Sign: } S = H(m)^{d} \bmod n \qquad \text{Verify: } H(m) \stackrel{?}{=} S^{e} \bmod 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.

O ⁣(exp((1.92+o(1))(lnn)1/3(lnlnn)2/3))O\!\left(\exp\left((1.92 + o(1))(\ln n)^{1/3}(\ln \ln n)^{2/3}\right)\right)

GNFS's running time — sub-exponential, but still growing fast enough that doubling n's bit length costs far more than double the effort.

Knowledge check

Test what you just learned →

3 quick questions, with an explanation for every answer.

Up next

RSA padding: OAEP, PKCS#1 v1.5, and why raw RSA fails

Textbook RSA is deterministic and malleable. Padding schemes are what actually make RSA encryption and signing safe to use in the real world.