Skip to main content
RSA explained

RSA explained

RSA is a public-key cryptosystem. A public key encrypts a message, and its corresponding private key decrypts it. Multiplying two primes is easy, but recovering the primes from a large product is difficult. This property underlies its security.

Reading the equations

gcd(a, b) is the greatest common divisor of a and b: the largest positive integer that divides both without a remainder. For example, the greatest common divisor of 12 and 18 is 6. Two numbers whose greatest common divisor is 1 are called coprime. The numbers themselves do not have to be prime.

gcd⁡(12,18)=6,gcd⁡(3,40)=1\gcd(12,18) = 6, \qquad \gcd(3,40) = 1

a mod n is the remainder when a is divided by n. For example, dividing 17 by 5 gives a quotient of 3 and a remainder of 2. RSA uses the remainder modulo n rather than the full value of a large power.

17=3×5+2,17 mod 5=217 = 3 \times 5 + 2, \qquad 17 \bmod 5 = 2

a ≡ b (mod n) means that a and b leave the same remainder when divided by n. For example, 17 and 2 both leave a remainder of 2 when divided by 5.

17≡2(mod5)17 \equiv 2 \pmod 5

1. Generate keys for text

Generate a 2048-bit RSA key for OAEP (SHA-256, MGF1-SHA-256, and an empty label). The small hand-calculation keys have insufficient space for OAEP. Keys and plaintext stay in your browser.

Generate keys to automatically encrypt and decrypt the text you enter.

2. Encrypt with the public key

UTF-8: 11 / 190 bytes

Encode the text as UTF-8 bytes M, then use OAEP to produce a 256-byte encoded message EM. OAEP uses a hash and a 32-byte random seed rather than simply adding zeros. Convert EM to integer m and encrypt it with RSA. The same text produces different ciphertext each time.

M=UTF8⁡(text),m=OS2IP⁡(OAEP⁡(M))M = \operatorname{UTF8}(\mathrm{text}),\quad m = \operatorname{OS2IP}(\operatorname{OAEP}(M))
∣M∣=11,∣PS∣=256−11−2×32−2=179|M| = 11,\quad |PS| = 256 - 11 - 2 \times 32 - 2 = 179

PS is the zero-filled string inside OAEP. Combine it with the empty-label hash (32 bytes), a delimiter (1 byte), and M, then apply an MGF1 mask derived from the random seed. EM consists of a leading byte, the masked seed (32 bytes), and masked data (223 bytes).

DB=SHA256⁡(ε) ∥ PS ∥ 01 ∥ MDB = \operatorname{SHA256}(\varepsilon)\,\|\,PS\,\|\,\mathtt{01}\,\|\,M
EM=00 ∥ maskedSeed ∥ maskedDB,c=m65537 mod nEM = \mathtt{00}\,\|\,maskedSeed\,\|\,maskedDB,\quad c = m^{65537}\bmod n

3. Decrypt with the private key

Recover integer m from ciphertext c using private exponent d and n. Convert it back to a 256-byte EM, validate and remove OAEP, then decode UTF-8 to recover the text. Public exponent e is not needed for decryption.

m=cd mod n,M=OAEPDecode⁡(I2OSP⁡(m,256))m = c^d \bmod n,\quad M = \operatorname{OAEPDecode}(\operatorname{I2OSP}(m,256))

4. PEM format and OpenSSL commands

The OpenSSL 3 example below generates a separate 2048-bit key and uses OAEP (SHA-256). Plaintext is limited to 190 bytes with this key size; combine RSA with symmetric encryption for longer data.

Generate a key and extract its public key

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem

Save the plaintext in message.txt, then run the following commands in order. private.pem is an unencrypted private key and must be kept secure.

Encrypt a file with the public key

openssl pkeyutl -encrypt -pubin -inkey public.pem -in message.txt -out encrypted.bin -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256

Decrypt a file with the private key

openssl pkeyutl -decrypt -inkey private.pem -in encrypted.bin -out decrypted.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256

The original plaintext is written to decrypted.txt. OAEP uses randomness, so encrypting the same plaintext produces different ciphertext each time.

Official OpenSSL documentation