Understanding RSA (Draft)

RSA is one of the first public-key encryption algorithms ever used. For this reason, it serves well as a pedagogical example for readers curious about cryptography.

In this post, I’ll explore the mathematical foundations of the system, with a focus on number theory.

Although this article leans toward the mathematical side, it’s written to invite coders and curious readers to engage with the concepts.

A basic understanding of arithmetic and modular arithmetic will be helpful. I'll link to external resources where appropriate, rather than covering every concept in depth.

For a more technical and programmer-oriented point of view, there are other resources out there. Here I've linked some basic implementations in JavaScript.

In mathematics, it's often useful to begin with simple cases to build intuition. For example, prime numbers can be understood at varying levels of complexity.

I’ll start with the most familiar definition, using basic examples to illustrate key ideas.

Another valuable habit is to revisit concepts multiple times. The more deeply you internalize them, the more naturally you’ll be able to apply them later on.


Let’s begin by understanding the system.

RSA relies on a pair of keys: one public, which can be shared openly, and one private, which must be kept secret by its owner.

The standard example involves Bob and Alice exchanging messages.
Since they want to encrypt their communication using RSA, each of them has such a key pair.

Confident in the system, Alice wants to send a message to Bob. She uses Bob’s public key to encrypt the message.

At this point, Bob (and Bob only) can decrypt the message using his private key.

The security of this system relies on the fact that Bob's private key is known only to him.

Using this private key, Bob can decrypt messages that were encrypted with his corresponding public key through a fascinating mathematical process based on modular arithmetic.


Key Generation

Generating such public/private key pairs involves fundamental number theory concepts, particularly prime numbers.
You may recall that a prime number is a positive integer greater than 1, divisible only by 1 and itself.

Example: Prime

33 and 77 are prime numbers, while 2121 is not. It can be expressed as a product of primes: 3×73 \times 7.

Prime numbers are the atoms of the integers. Every positive integer can be constructed as a product of primes (factorization).

Example: Prime factorization

21=7×321 = 7 \times 3 or 3×73 \times 7

This is formalized in a famous result known as the Fundamental Theorem of Arithmetic, or Unique Factorization Theorem (UFT).

Theorem: Unique Factorization Theorem

Every positive integer can be uniquely factorized (up to ordering) into its prime factors.

Remark

Notice that 1 is not included in the theorem’s statement, as it can be seen as a product of no primes.

Another example of prime factorization:

Example: Prime factorization

6615=33×72×56615 = 3^3 \times 7^2 \times 5

Now let’s return to RSA and the key generation process.
Bob can generate a pair of keys using the following steps:

Definition: RSA Key Generation
  1. Choose two primes: pp and qq
  2. Compute n=p×qn = p \times q
  3. Compute ϕ(n)=(p1)(q1)\phi(n) = (p − 1)(q − 1)
  4. Choose ee such that 1<e<ϕ(n)1 < e < \phi(n) where ee is coprime to ϕ(n)\phi(n) (gcd(e,ϕ(n))=1gcd(e, \phi(n)) = 1)
  5. Find dd such that e×d1(modϕ(n))e \times d \equiv 1 \pmod{\phi(n)}
  6. The public key is (e,n)(e, n); the private key consists of (d,p,q)(d, p, q)

Some of these steps rely on concepts we haven’t yet defined.
Let’s walk through them one by one.

Step 1: Choose two primes

Bob first chooses two distinct prime numbers: pp and qq.

These primes should be large enough (at least 20 digits) to ensure the system’s security.
It is notoriously difficult to factor the product of two large primes and this is the basis of RSA’s strength.

Step 2: Compute n=p×qn = p \times q

This is straightforward.

Step 3: Compute ϕ(n)=(p1)(q1)\phi(n) = (p − 1)(q − 1)

Here we introduce ϕ(n)\phi(n), the Euler's totient function.

Definition: Euler's Totient Function

Given a positive integer nn, the totient function ϕ(n)\phi(n) counts the number of integers from 00 to n1n - 1 that are coprime to nn. By convention, ϕ(1)=1\phi(1) = 1.

Example

ϕ(12)=4\phi(12) = 4 because the integers 1,5,71, 5, 7, and 1111 are coprime to 1212.

But what does “coprime” mean?

Two positive integers are coprime if they have no common prime factors other than 1.

In particular, if nn is a prime number, then ϕ(n)=n1\phi(n) = n - 1. Why?

It also follows that when pp and qq are coprime (distinct primes are coprime) ϕ(n)=(p1)(q1)\phi(n) = (p − 1)(q − 1).

Note: computing ϕ(n)\phi(n) from just nn would require factoring nn, which is copmputationlly hard (recall step 1) if you don't know pp and qq.

Step 4: Choose ee such that 1<e<ϕ(n)1 < e < \phi(n) where ee is coprime to ϕ(n)\phi(n) (gcd(e,ϕ(n))=1gcd(e, \phi(n)) = 1)

Now Bob must choose an exponent ee.

It should satisfy:

  • 1<e<ϕ(n)1 < e < \phi(n)
  • ee is coprime to ϕ(n)\phi(n) or, algorithmically, gcd(e,ϕ(n))=1gcd(e, \phi(n)) = 1

A common choice is e=216+1e = 2^{16} + 1 (65,537); large enough for security, small enough for efficiency.

To check whether ee and ϕ(n)\phi(n) are coprime, Bob can use the greatest common divisor (gcd).

Definition: GCD

The greatest common divisor of two integers is the largest positive integer that divides both numbers without leaving a remainder.

Example: GCD

The gcd of 1212 and 1515 is 33, since it divides both without a remainder.

To compute the gcd, Bob can use the Euclidean algorithm, one of the oldest known algorithms (from Elements, c. 300 BC). The algorithm is based on the following principle:

Theorem

If a0a \neq 0 and b=aq+rb = aq + r for some qq and rr, then gcd(a,b)=gcd(a,r)\gcd(a, b) = \gcd(a, r)

It's extremely easy top write the algorithm in javascript:

  1. Let aa and bb be two positive integers (swap if b>ab > a)
  2. If b=0b = 0, then gcd(a,b)=a\gcd(a, b) = a
  3. Otherwise, replace aa with bb, and bb with rr
  4. Repeat until b=0b = 0

rr is the reminder computed using javascript remainder operator %. The last non-zero remainder is the gcd.

function gcd(a, b) {
  return b === 0 ? a : gcd(b, a % b);
}

Notice the nice recursion! Now, Bob can algorithmically determine whether two integers are coprime:

Definition: Coprime

Two integers are coprime if their greatest common divisor (gcd) is 1.

Example: Coprime

The integers 8 and 15 are coprime because their gcd is 1.

Step 5: Find dd such that e×d1(modϕ(n))e \times d \equiv 1 \pmod{\phi(n)}

The equation e×d=1modϕ(n)e \times d = 1 \mod \phi(n) must be read as e×de \times d is congruent 11 modulo ϕ(n)\phi(n).

We now delve into modular arithmetic, starting with its foundation: modular congruence.

Definition: Modular Congruence

Let aa and bb be integers, and let mm be a positive integer. We say that aa is congruent to bb modulo mm (written as ab(modm)a \equiv b \pmod m), if there exists an integer kk such that: a=b+kma = b + km.

Nothe: bb is the remainder when dividing aa by mm.

JavaScript doesn’t have a true mod operator, only a remainder operator %, which behaves slightly differently for negative numbers.
(See this StackOverflow post).

There is anyway a proposal.

Now, the equation

e×d1(modϕ(n)) e \times d \equiv 1 \pmod{\phi(n)}

can be rewritten as:

de1(modϕ(n)) d \equiv e^{-1} \pmod{\phi(n)}

That is: dd is the modular inverse of ee modulo ϕ(n)\phi(n).

Such a modular inverse exists only if ee and ϕ(n)\phi(n) are coprime (and by step 4 they are).

To compute dd, Bob can use the Extended Euclidean Algorithm, a standard method for finding modular inverses.

For theoretical reasons, I'll introduce a more elegant method based on Euler's Theorem, which provides deeper insight into why RSA encryption\decription works so smootlhy.

Theorem: Euler's Theorem

Let mm be a positive integer. For any integer aa that is coprime to mm, we have:

aϕ(m)1(modm) a^{ \phi(m) } \equiv 1 \pmod m

Since Bob knows ϕ(n)\phi(n), he can use Euler's Theorem to compute e1e^{-1}:

eϕ(n)1(modn) e^{\phi(n)} \equiv 1 \pmod n

This implies:

eϕ(n)1e1(modn) e^{\phi(n) - 1} \equiv e^{-1} \pmod n

So Bob can compute:

d=eϕ(n)1(modϕ(n)) d = e^{\phi(n) - 1} \pmod{\phi(n)}

to obtain the modular inverse.

eϕ(n)1e^{\phi(n) - 1} is calculated through the fast exponentiation algorithm (also known as exponentiation by squaring or binary exponentiation)


Encryption/Decritpion

Encryption

Now Bob have already shared his public-key (e,n)(e,n) to Alice.

Alice will be able to send a message to Bob in the following way:

  • She converts the message into a series of chuncks, each of wich is represented by an integer mm ranging from 11 to nn
  • To encrypt each chunk cc, she compute c=me(modn) c = m^{e} \pmod n

Decritpion

Bob can decrypt each received chunk using the formula:

cd(modn)=(me)d(modn)=mde(modn) c^d \pmod n = (m^e)^d \pmod n = m^{de} \pmod n

Remember that dd is the modular inverse (e1e^{-1}) of ee. Thus de1(modϕ(n))de \equiv 1 \pmod{\phi(n)}, and the equation becomes:

mde(modn)=m1(modϕ(n))(modn) m^{de} \pmod n = m^{1 \pmod {\phi(n)}} \pmod n

By definition of modular congruence, 1(modϕ(n))1 \pmod {\phi(n)} can be written as 1+k×ϕ(n)1 + k \times \phi(n) for some kk:

m1(modϕ(n))(modn)=m1+k×ϕ(n)(modn) m^{1 \pmod {\phi(n)}} \pmod n = m^{1 + k \times \phi(n)} \pmod n

Using the power properties:

m1+k×ϕ(n)(modn)=m1mkϕ(n)(modn)=m(mϕ(n))k(modn) m^{1 + k \times \phi(n)} \pmod n = m^1*m^{k*\phi(n)} \pmod n= m*(m^{\phi(n)})^k \pmod n

We introduced Euler's Theorem for a reason. In fact, in number theory it's used every time when dealing with powers as it simplifies the problem significantly. Euler tells us that mϕ(n)=1(modn)m^{\phi(n)} = 1 \pmod n, thus:

m(mϕ(n))k(modn)=m(1)k(modn)=m(modn) m*(m^{\phi(n)})^k \pmod n = m*(1)^k \pmod n = m \pmod n

Thus for Bob, recovering the message for each chunk cc will simply evaluate to:

cd(modn)=m(modn) c^d \pmod n = m \pmod n

Exercise: Rewrite the encryption and decryption process by yourself, focusing only on one chunk as explained.

Conclusion

I hope to have explained the theoretical part of RSA for generating keys and encryption/decryption. For users encountering this concept for the first time, it will certainly be overwhelming and that's normal. Hopefully, you can come back to this while learning the basic concepts.