其实任何加密算法都有1个原则, 就是正向运算简单, 反向推算很难。
我们假设 x 是原数据, y是加密后的数据, f()是加密算法.
那么f()必须满足 y = f ( x ) y = f(x) y=f(x) , 而且f的运算要相对于简单。
而对应的反函数 y = f − 1 ( x ) y = {f^{-1}}(x) y=f−1(x) 则要求很复杂.
例如 f ( x ) = x 7 f(x) = x^7 f(x)=x7 是不适和作为加密算法的.
因为对应的反函数 f ( x ) = x 7 f(x) = \sqrt[7]x f(x)=7x 对于计算机来讲并不复杂, 这意味这么加密后的数据很容易破解.
在当今数学中, 什么最难, 数论肯定是候选的答案之一, 而数论是主要研究质数的, 也就是讲我们可以利用质数难以逆运算的特性来获得一些加密算法
看看下面这个算法, 下面出现的字符 x y e n 等都是整数, 而 e, n 是已知的常量, 且都是质数
y = f ( x ) = x e m o d n y = f(x) = x^e \mod n y=f(x)=xemodn
这个算法的正向算法是不复杂的, 只不过是求 x 的 e次方 后再除以 n 后的余数 就是y
但是反过来已知y的话, 反推 x 的值就不简单了,特别是如果 y , e 与 n都是1个相当大的质数的时候(1024/2048 bit)
问题来了, 上面的算法既然反运算这么困难, 那么如何解密呢
所以这就是RSA神奇的地方, 其实下数学式中, y表示加密后的值, x是原来的值, 而e 和 n的组合[e, n] 就是公钥public key
y = f ( x ) = x e m o d n y = f(x) = x^e \mod n y=f(x)=xemodn
假设存在另1个private key [d, n], 如果已知加密后的y值的话, 可以用相同的公式
x = g ( y ) = y d m o d n x = g(y) = y^d \mod n x=g(y)=ydmodn 来求出x 完成解密
那么[d, n]就是所谓的私钥.
也就是讲, 虽然这个算法逆运算很难, 但是我们可以通过1些规则生成一对 公钥 和 私钥 .
利用 公钥可以 1个相当简单的数学式求得 加密值
而且 也可以 利用 私钥 和相同的数学式 来求得 加密钱的值.
其实这就是RSA的基本原理了.
而本质就是我们用质数去求积是很简单的, 但是反过来大质数分解就相当困难。
注:
大质数分解是指将一个大的合数分解成它的质因数的乘积的过程。在密码学中,大质数分解是一种重要的数学问题,因为许多加密算法的安全性都基于这个问题的困难性。
具体来说,假设有一个大的合数n,它是两个大的质数p和q的乘积,即n = p * q。如果p和q都非常大,那么将n分解成p和q的乘积是非常困难的。这是因为目前已知的最好的分解算法需要指数级的时间和计算资源,随着p和q的位数增加,算法的时间和资源需求呈指数级增长。
因此,大质数分解被认为是一种“困难问题”,即在可接受的时间内无法解决的问题。这个问题的困难性是许多加密算法的基础,包括RSA算法、Rabin加密算法、Goldwasser-Micali加密算法等。这些算法的安全性基于假设,即在合理的时间内无法分解大的合数。
需要注意的是,虽然大质数分解被认为是困难问题,但这并不意味着它在未来永远无法被解决。随着计算技术的不断进步和新的算法的发展,有可能会出现更快速的分解算法,从而导致这些加密算法的不安全性。因此,密钥的长度需要根据当前计算技术的发展和安全需求的变化而不断调整。
现在我们已经通过一些规则(下面介绍), 获得了RSA算法的一对 public key 和 private key
分别是
public key = [3, 51] 则 e = 3, n = 51
private key = [11, 51] 则 d = 11, n = 51
它们都是质数
下面我们用RSA算法和public key [3, 51] 来加密 1个数字21, 则 x = 21
所以 y = x e m o d n = 2 1 3 m o d 51 = 9261 m o d 51 = 30 y = x^e \mod n = 21^3 \mod 51 = 9261 \mod 51 =30 y=xemodn=213mod51=9261mod51=30
也就是 先求 2 1 3 = 9261 21^3 = 9261 213=9261 然后求9261 除以 51 的余数, 因为181 * 51 + 30 = 9261, 所以余数是30
也就是 加密后的值 y = 30
下面我们用RSA算法和private key [11, 51] 来解密上面加密后的值30
所以 x = y d m o d n = 3 0 1 1 m o d 51 = 17714700000000000 m o d 51 = 21 x = y^d \mod n = 30^11 \mod 51 = 17714700000000000 \mod 51 =21 x=ydmodn=3011mod51=17714700000000000mod51=21
也就是 先求 3 0 11 = 17714700000000000 30 ^{11} = 17714700000000000 3011=17714700000000000 然后求 17714700000000000 除以 51 的余数, 因为347347058823529 * 51 + 21 = 17714700000000000, 所以余数是 21
也就是 求得加密前的值 x = 21
是不是很神奇, 但是实际上 e d 和 n都是非常大的质数, 即使使用超级计算在不知道d 的值情况下 去穷举也很难求得 用e 加密后值的原来值的.
对关键上面的[3, 51], [11,51] 这对keys 是如何获得的呢?
这个就是RSA算法的核心了, 它用到了欧拉定理。
包含下面5个规则
上面例子是 我们选择 两个质数3 和 17 则p=3 q=17
第2步 , n = 51
第3步 , t = (3-1) * (17 - 1) = 32
第4步, 选择 e = 3 1 < 3 < 32 且 3 不是32 的因数
第5步,选择 d = 11 3 * 11 除以 32 的余数正好是1
所以 得到 公钥 = [e, d] = [3, 51]
私钥 = [d, n] = [11, 51]
我们先写1个 类 KeyGenerator 用于生成 公钥和私钥
KeyGenerator.py
from sympy import isprime
from src.common.annotation.ToString import to_string
'''
这个类是用于demo 生成 一对 公钥 私钥
步骤:
1. 选择两个质数 p q
2. 求得中间参数 n = p * q
3. 求得中间参数 t =(p-1) * (q-1)
4. 选择1个质数e, 1 < e < t, 而且 e 与 t互质
5. 计算e的模数反元素d, 即是满足 ed mod t = 1
则公钥是 [e, n], 私钥是 [e, d]
====================
下面内容不在这个类中
加密时,将明文m转换为整数,使用公钥加密,即 c ≡ m^e (mod n)
解密时,使用私钥解密,即 m ≡ c^d (mod n)
'''
@to_string
class KeyGenerator:
def __init__(self, p, q):
self._p = p
self._q = q
self.__validate_prams()
@property
def p(self):
return self._p
@property
def q(self):
return self._q
def __validate_prams(self):
"""
verfiy p q, both of them must be prime
"""
if not isprime(self.p) or not isprime(self.q):
raise ValueError("p and q must be primes!")
def get_n_pram(self):
"""
step 2, n = pq
"""
return self.p * self.q
def get_t_pram(self):
"""
step 3, t = (p-1)(q-l)
"""
p = self.p
q = self.q
return (p - 1) * (q - 1)
def get_pub_key(self):
"""
step 4, choose a prime e, e must meet below 3 conditions
1. e is a prime
2. 1 < e < t
3. e is not a factor of t
then public key is [e, n]
"""
n = self.get_n_pram()
t = self.get_t_pram()
e = -1
for i in range(2, t):
if isprime(i) and t % i != 0:
e = i
break
if e < 0:
raise ValueError("cannot find a prime for pub key, please adjust the p q")
return [e, n]
def get_pri_key(self, pub_key):
"""
step 5, choose a prime d
de mod t = 1 and d != e
then private key is [d, n]
"""
n = self.get_n_pram()
t = self.get_t_pram()
e = pub_key[0]
d = 1
while (d * e) % t != 1 or d == e:
d = d + 1
return [d, n]
下面测试代码用于生成一对公钥私钥
p = 3
q = 17
key_generator = KeyGenerator(p, q)
public_key = key_generator.get_pub_key()
private_key = key_generator.get_pri_key(public_key)
print("public key is " + str(public_key))
print("private key is " + str(private_key))
输出
public key is [3, 51]
private key is [11, 51]
我们再写两个类, 1个用于加密, 1个用于解密
加密类
class RsaEncryptor:
def __init__(self, pub_key):
self._pub_key = pub_key
@property
def pub_key(self):
return self._pub_key
@pub_key.setter
def pub_key(self, value):
self._pub_key = value
def encrypt(self, num):
"""
假设公钥是[e, n]
则 加密时,将明文m转换为整数,使用公钥加密,即 c ≡ m^e (mod n)
"""
e = self.pub_key[0]
n = self.pub_key[1]
return num ** e % n
解密类
class RsaDecrypter:
def __init__(self, pri_key):
self._pri_key = pri_key
@property
def pri_key(self):
return self._pri_key
@pri_key.setter
def pri_key(self, value):
self._pri_key = value
def decrypt(self, num):
"""
假设私钥是[d, n]
则 解密时, 使用的算法是 m ≡ c^d (mod n), 与加密是同样算法
"""
d = self.pri_key[0]
n = self.pri_key[1]
return num ** d % n
测试代码:
num = 21
public_key = [3, 51]
private_key = [11, 51]
encryptor = RsaEncryptor(public_key)
decrypter = RsaDecrypter(private_key)
encrypted_num = encryptor.encrypt(num)
decrypter_num = decrypter.decrypt(encrypted_num)
print("original number is " + str(num))
print("encrypted number is " + str(encrypted_num))
print("decrypted number is " + str(decrypter_num))