# Brief introduction of RSA encryption algorithm

RSA is a public key encryption algorithm. It has two keys: public key and private key. The public key is used for encryption and is public. The private key is used for decryption and is not public. It does not need to be transmitted with data, so as to prevent the key from being leaked during network transmission.

The principle of RSA algorithm design is to rely on modular exponentiation, such as encryption, decryption and key generation.

## 1. Key design

First, we need to understand the idea of key design:
① Encryption calculation: c = m^e mod n;
② Decryption calculation: m = c^d mod n;
Where m is plaintext, c is ciphertext, e is public key, d is private key, and n is a large number we want to generate.

Therefore, according to the above two formulas:
dk( ek(m) ) = m
= (m^e mod n) ^d mod n        # Here is encryption before decryption
= m^(ed) mod n
Then, to decrypt the generated data as plaintext, * * m^(ed) mod n must be equal to M. ①**

m^φ(n) mod n = 1 → m ^(tφ(n)) mod n = 1 → m ^(tφ(n)+1) mod n = m ②
The two formulas in ① and ② correspond, i.e. ed = (t)* φ (n)+1) → ed = 1 mod φ (n).
It is not difficult to see that the public key e and the private key d are about φ (n) Reciprocal.

Here, you may wonder why others can't use the public key e and e φ (n) To get the private key directly?, This is what we will talk about next: how to generate N, which makes it difficult for others to calculate φ (n).

## 2. Generation of large number n

First, we need to know a theorem: if n = p*q and P, q are prime numbers, then φ (n) = φ (p) * φ (q) = (p-1) * (q-1)
Moreover, we know that it is very difficult to decompose a large number, so we need to find two large numbers p and q to make the generated n difficult to be decomposed and calculated, and we need p and q to be prime numbers to meet the above theorem, so our problem becomes how to generate large prime numbers p and q.

## 3. Generate large prime numbers p, q

Our idea can be as follows: to generate a large prime number, we can first generate a large number with a random number, and then judge whether it is a prime number.
Generating large numbers is simple, and the random library can be implemented, so how to verify whether they are prime numbers? Here, we can't use a simple prime discrimination method: for example, traverse all numbers smaller than it and take the remainder to see whether it is 0. Because this is only applicable to decimals. For large numbers, the calculation time is too long and it doesn't work at all. So here we need to use a prime detection method: Miller Rabin algorithm

Miller Rabin algorithm idea:
Firstly, according to Fermat's small theorem: a^p-1 = 1 mod p (P is a prime number), we can write p-1 in the form of 2 ^k*m, where a can be generated randomly, but it needs to meet 1 < = a < = n-1.
Then a ^p-1 = ((a ^m) ^2) ^2..., so we can calculate whether the result of a ^m mod p is ± 1 a priori,
If yes, then a ^p-1 = ((a ^m) ^2) ^2... Must be 1, then it can be judged that p is a prime number;
If not, let b=a^ m mod p, and then calculate b, b^ 2,b^ 4,..., b^ 2^(k-1) mod n successively. If one is found to be ± 1, P is a prime number, otherwise, P is a composite number.

However, this algorithm is not 100% correct, and sometimes the composite number is output as a prime number. Therefore, in general, we can generate pseudo-random numbers for prime detection instead of random numbers.

The specific code of Miller Rabin algorithm is as follows:

```#Judge whether it is a prime number (Miller rabbit). RoundTime indicates the number of cyclic tests to improve the accuracy
def IsPrime(BigNum, RoundTime):
temp = BigNum - 1
k = 0
while (temp & 0x1)==0 and temp:
temp = temp >>1
k = k+1
m = temp

while RoundTime:
a = random.randint(1, BigNum- 2)
b =  repeatMod(a, m, BigNum)
if b == 1 or b==BigNum-1:
return True

for i in range(1,k):
b = repeatMod(b, 2, BigNum) #If b^2^(k-1) mod n ==+-1, it is a prime number
if b == 1 or b==BigNum-1:
return True

RoundTime = RoundTime -1
return False
```

## 4. Key generation

After we generate two large prime numbers p, q, we can get it directly φ (n). For the public key e, we can generate it randomly, but we need to meet gcd(e, φ (n))=1 (Euclidean division can be used to determine whether the remainder is 1).

Then for private key d, because ed = 1 mod φ (n) So we only need to find the inverse element to get d, but the inverse element can be obtained according to the inverse process of Euclidean division. The inverse code of Euclidean division is as follows:

``` #Find X and y are the two inverse elements, where y is the private key
def get_(a, b):
if b == 0:
return 1, 0
else:
k = a // b
x1, y1 = get_(b, a % b)
x, y = y1, (x1 - k * y1)
return x,y
```

## 5. Encryption and decryption

In the encryption and decryption process, we only need to:
① Encryption calculation: c = m^e mod n;
② Decryption calculation: m = c^d mod n; Just calculate it.

However, for the calculation of large numbers, we can not directly use the calculation given by the high-level language, which will also lead to too long time. We need to use the square multiplication algorithm. The specific code of the algorithm is as follows:

```#Square multiplication algorithm for remainder base^n mod mod
def repeatMod(base, n, mod):
a = 1
while n:
if n&1:
a = (a*base)%mod
base = (base*base)%mod
n = n>>1
return a
```

## All codes

```#coding=gbk
import math
import random

#Square multiplication algorithm for remainder base^n mod mod
def repeatMod(base, n, mod):
a = 1
while n:
if n&1:
a = (a*base)%mod
base = (base*base)%mod
n = n>>1
return a

#Judge whether it is a prime number (Miller rabbit)
def IsPrime(BigNum, RoundTime):
temp = BigNum - 1
k = 0
while (temp & 0x1)==0 and temp:
temp = temp >>1
k = k+1

m = temp

while RoundTime:
a = random.randint(1, BigNum- 2)
b =  repeatMod(a, m, BigNum)

if b == 1 or b==BigNum-1:
return True

for i in range(1,k):
b = repeatMod(b, 2, BigNum) #If b^2^(k-1) mod n ==+-1, it is a prime number
if b == 1 or b==BigNum-1:
return True

RoundTime = RoundTime -1

return False

#Generating large prime numbers
def BuildBigPrime():
flag = False
while not flag:
BigNum = random.randint(2**512,2**513)
if BigNum % 2 !=0 :
flag = IsPrime(BigNum, 10)

return BigNum

#Finding the greatest common divisor by rolling and dividing
def MaxCommDivisor(m,n):
if n == 1:
return True
if m % n == 0:
return False
else:
flag = MaxCommDivisor(n, m%n)
return flag

#Finding the inverse of b mod a b
def get_(a, b):
if b == 0:
return 1, 0
else:
k = a // b
x1, y1 = get_(b, a % b)
x, y = y1, (x1 - k * y1)
return x,y

if __name__ == '__main__':
print("Generating large prime numbers------")
p = BuildBigPrime()
q = BuildBigPrime()
while p==q:
q = BuildBigPrime()

print("Large prime p:",p,"\n Large prime q:",q)
n = p*q
fn = (p-1)*(q-1)

while True:
key1 = random.randint(2**64,2**65)
if  MaxCommDivisor(fn, key1):
break

print("Key 1 is:",key1)
k, key2 = get_(fn,key1)
#The generated private key key2 may be negative. We need mod fn to ensure that it is positive
if key2<0:
key2 = key2 % fn

print("Key 2 is:",key2)

m = input("Please enter clear text:")
m = int(m.encode('utf-8').hex(),16)

print('\n------Encryption in progress----')
c = repeatMod(m,key1,n)
print("In clear text:",m,"\n The ciphertext is:",c)

print('\n------Decrypting----')
m = repeatMod(c,key2,n)
print("The ciphertext is:",c,"\n In clear text:",m)
```

Tags: Python Algorithm rsa

Posted on Sat, 09 Oct 2021 08:30:21 -0400 by visionmaster