RSA加解密算法及python代码实现

写在前面:本代码只需调用random库,关于逆元、素数、模幂等的求解均为自编函数。

1. RSA算法描述

1.1 密钥的产生

  (1)选两个保密的大素数 p p p q q q
  (2)计算 n = p × q ,   φ ( n ) = ( p − 1 ) ( q − 1 ) n=p\times q,\ \varphi \left( n \right) =\left( p-1 \right) \left( q-1 \right) n=p×q, φ(n)=(p1)(q1),其中 φ ( n ) \varphi \left( n \right) φ(n) n n n的欧拉函数;
  (3)选一整数 e e e,满足 1 < e < φ ( n ) 1<e<\varphi \left( n \right) 1<e<φ(n),且 gcd ( φ ( n ) , e ) = 1 \text{gcd}\left( \varphi \left( n \right) ,e \right) =1 gcd(φ(n),e)=1
  (4)计算 d d d,满足
d ⋅ e   ≡   1  mod  φ ( n ) d\cdot e\ \equiv \ 1\ \text{mod\ }\varphi \left( n \right) de  1 mod φ(n)
  即 d d d e e e在模 φ ( n ) \varphi \left( n \right) φ(n)下的乘法逆元,因 e e e φ ( n ) \varphi \left( n \right) φ(n)互素,由模运算可知,它的乘法逆元一定存在。
  (5)以 { e , n } \{e,n\} {e,n}为公钥, { d , n } \{d,n\} {d,n}为私钥。

1.2 加密

  加密时首先将明文比特串分组,使得每个分组对应的十进制数小于 n n n,即分组长度小于 log ⁡ 2 n \log _2n log2n。然后对每个明文分组 m m m,作加密运算:
c ≡ m e  mod  n c\equiv m^e\ \text{mod\ }n cme mod n

1.3 解密

  对密文分组的解密运算为:
m ≡ c d  mod  n m\equiv c^d\ \text{mod\ }n mcd mod n

1.4 RSA算法中解密过程的正确性证明

2. 辅助模块算法

2.1 模重复平方算法

  RSA的加密和解密过程都为求一个整数的整数次幂,再取模。如果按其含义直接计算,则中间结果非常大,有可能超出计算机所允许的整数取值范围。而用模运算的性质:
( a × b )  mod  n = [ ( a mod  n ) × ( b mod  n ) ] mod  n \left( a\times b \right) \ \text{mod\ }n=\left[ \left( a\text{mod\ }n \right) \times \left( b\text{mod\ }n \right) \right] \text{mod\ }n (a×b) mod n=[(amod n)×(bmod n)]mod n
就可减小中间结果。再者,考虑如何提高加密和解密运算中指数运算的有效性。
  本文采用模重复平方算法求解形如 b n mod  m b^n\text{mod\ }m bnmod m的式子:
  一般,求 b n b^n bn可如下进行,其中, b b b n n n是正整数:
  将 n n n表示为二进制: n k − 1 , n k − 2 , . . . n 1 , n 0 \boldsymbol{n}_{k-1},\boldsymbol{n}_{k-2},...\boldsymbol{n}_1,\boldsymbol{n}_0 nk1,nk2,...n1,n0,其中 n i ∈ { 0 , 1 } , i = 0 , 1 , . . . , k − 1 n_i\in \{0,1\},i=0,1,...,k-1 ni{0,1},i=0,1,...,k1。则
n = n 0 + n 1 2 + ⋯ + n k − 1 2 k − 1 n=n_0+n_12+\cdots +n_{k-1}2^{k-1} n=n0+n12++nk12k1
  则 b n mod  m b^n\text{mod\ }m bnmod m的计算可归纳为
b n ≡ b n 0 ( b 2 ) n 1 ⋯ ⋯ ( b 2 k − 2 ) n k − 2 ⋅ ( b 2 k − 1 ) n k − 1 ( mod  m ) b^n\equiv b^{n_0}\left( b^2 \right) ^{n_1}\cdots \cdots \left( b^{2^{k-2}} \right) ^{n_{k-2}}\cdot \left( b^{2^{k-1}} \right) ^{n_{k-1}}\left( \text{mod\ }m \right) bnbn0(b2)n1(b2k2)nk2(b2k1)nk1(mod m)
  我们最多作 2 [ log ⁡ 2 n ] 2\left[ \log _2n \right] 2[log2n]次乘法,这个计算方法叫做“模重复平方算法”。
  故该模块的代码为:

def fastExpMod(b,n,m):
    '''
    return : b^n mod m 
    '''
    result = 1
    while n != 0:
        if (n & 1) == 1: #按位与&操作
            result = (result * b) % m
        b = (b*b) % m
        n = n >> 1      #位数右移>>操作
    return result
2.2 欧几里得算法

  在密钥的生成过程中,需要求解 e e e模逆运算,利用欧几里得算法可以求解,具体原理本文不再赘述,只给出算法步骤如下:

故该模块的代码为:

def Euclid(a,b):
    '''
    欧几里得算法 ax + by = gcd(a,b) 
    Return : [x , y , gcd(a,b)]
    注:a与b互素时,x为a模b的逆元
    '''
    X = [1,0,a]
    Y = [0,1,b]
    while Y[2] !=0 :
        Q = X[2]//Y[2]
        NEW_Y = [i*Q for i in Y]
        T = list(map(lambda x: x[0]-x[1], zip(X, NEW_Y))) # X和NEW_Y做相减操作
        X = Y.copy()
        Y = T.copy()
    return X           
2.3 素性检验算法

  素性检验算法种类较多,本文采用费马素性检测算法判断随机生成的大整数是不是素数。读者可以选择其他类型的素性检验算法,例如Miller-Rabin(n)等。

Fermat小定理:
  给定素数 p , a ∈ Z p,a\in Z p,aZ,则有 a p − 1 ≡ 1 ( mod  p ) a^{p-1}\equiv 1\left( \text{mod\ }p \right) ap11(mod p)
Fermat小定理推论:
  如果有一个整数 a a a ( a , m ) = 1 \left( a,m \right) =1 (a,m)=1,使得 a m − 1 ≡ 1 ( mod  m ) a^{m-1}\equiv 1\left( \text{mod\ }m \right) am11(mod m),则 m m m至少有 1 / 2 1/2 1/2的概率为素数。

  根据Fermat小定理及其推论,给定任意一个大整数 m m m以及安全参数 k k k,我们便可以判断该大整数的素性。Fermat素性检验算法步骤具体如下:

  Step1:若 m m m为偶数,跳出程序,得出结论 m m m为合数;否则,继续执行Step2;
  Step2:随机选取整数 a a a,使得 2 ≤ a ≤ m − 2 2\le a\le m-2 2am2
  Step3:计算 a a a m m m的最大公因数 g = ( a , m ) g=\left( a,m \right) g=(a,m),如果 g = 1 g=1 g=1,继续执行;否则跳出,认为 m m m为合数;
  Step4:计算 r = a m − 1 ( mod  m ) r=a^{m-1}\left( \text{mod\ }m \right) r=am1(mod m),如果 r = 1 r=1 r=1 m m m可能为素数跳转执行Step2;否则跳出, m m m为合数;
  Step5:重复上述Step2-4过程 k k k次,如果每次得到 m m m均可能为素数,则 为素数的概率为 1 − 1 2 k 1-\frac{1}{2^k} 12k1

  故该模块的代码为:

def fermatPrimeTest(m,k):
    '''
    费马素性检验算法
    m : 给定整数
    k : 安全参数,重复K次
    '''
    if m % 2==0:
        return False
    for i in range(k):
        a = random.randint(2,m-2)
        g = Euclid(a, m) #欧几里得见2.2
        if g[2] == 1:
            r = fastExpMod(a,m-1,m) #模重复平方算法见2.1
            if r ==1:
                continue
            else:
                return False
        else:
            return False
    return True

3. 源代码

'''
RSA加解密算法
2020.11.28
1.模平方算法   2.欧几里得算法   3.费马素性检测算法 
'''
import random
def fastExpMod(b,n,m):
    '''
    return : b^n mod m 
    '''
    result = 1
    while n != 0:
        if (n & 1) == 1: #按位与操作
            result = (result * b) % m
        b = (b*b) % m
        n = n >> 1      #位数右移操作
    return result
            
def Euclid(a,b):
    '''
    欧几里得算法 ax + by = gcd(a,b) 
    Return : [x , y , gcd(a,b)]
    '''
    X = [1,0,a]
    Y = [0,1,b]
    while Y[2] !=0 :
        Q = X[2]//Y[2]
        NEW_Y = [i*Q for i in Y]
        T = list(map(lambda x: x[0]-x[1], zip(X, NEW_Y)))
        X = Y.copy()
        Y = T.copy()
    return X
           
def fermatPrimeTest(m,k):
    '''
    费马素性检验算法
    m : 给定整数
    k : 安全参数,重复K次
    '''
    if m % 2==0:
        return False
    for i in range(k):
        a = random.randint(2,m-2)
        g = Euclid(a, m)
        if g[2] == 1:
            r = fastExpMod(a,m-1,m)
            if r ==1:
                continue
            else:
                return False
        else:
            return False
    return True

def findPrime(lower,upper):
    ''' 
    return : 一个位于upper和lower之间的素数 
    '''
    while True:
        n = random.randint(lower, upper)
        if fermatPrimeTest(n,6) == True :
            return n

def selectE(fn):
    '''
    fn : euler function
    Return : e
    '''
    while True:
        e = random.randint(1, fn)
        temp = Euclid(e,fn)
        if temp[2] == 1:
            return e
    
def keyGenerate(lower,upper):
    '''
    给定两个素数p和q生成的区间
    return : e,n,d
    '''
    p = findPrime(lower, upper)
    q = findPrime(lower, upper)
    print("p:"+str(p)+"   q:"+str(q))
    # print("q:"+str(q))
    n = p*q
    fn = (p-1)*(q-1)
    e = selectE(fn)
    temp = Euclid(e, fn) # 欧几里得算法求逆元
    d = temp[0]
    if d < 0:       # 由于e和fn互素故一定存在逆元
        d = d + fn  # 保证d为正数
    return e,n,d

def start():
     e,n,d = keyGenerate(1000,10000) # 密钥生成
     #更改keyGenerate函数的两个参数,可以改变生成素数的位数大小。
     print("public key (e,n):",end="")
     print("("+str(e)+"  ,  "+str(n)+")\n")
     print("private key d: "+str(d)+"\n")
     m = random.randint(1, n)     # m < n m为明文
     print("Plaintext: "+str(m))
     c = fastExpMod(m, e, n)      # 加密  c为密文 m^e mod n
     print("\nEncryption of PlainText: "+str(c))
     x = fastExpMod(c, d, n)      # 解密 c^d mod n
     print("\nDecryption of CipherText: "+str(x))
     if x == m:
         print("\nThe plaintext and ciphertext are the same.")
         
if __name__ =="__main__":  
    start()

结果测试:

测试一(设定p,q是位于 2 511 ∼ 2 512 2^{511}\sim 2^{512} 25112512之间的大素数)

测试二 (设定p,q位于1000到10000之间)

参考文献

RSA算法python实现

模指数运算

另:RSA算法本身被证明是安全的,但若RSA的参数选取不当,会带来很多的安全隐患。例如共模攻击、低加密指数攻击、费马分解法、因数碰撞攻击等,读者可以参考2016年全国大学生密码技术竞赛(RSA 加密体制破译)赛题三,本文不再赘述。

RSA_Breaking Code

2016密码挑战赛(RSA 加密体制破译)解题过程

  • 9
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值