利用python实现RSA算法

什么是RSA算法

涉及到的python运算

求模运算(%):是将两个数相除,并返回余数

random.choice(x):从指定的序列x中随机获取一个数据

幂运算:pow(x, y)指x的y次方

注意当pow(x, y, z)有三个参数时,表示(x^y)%z

math.gcd(x, y):返回给定的整数参数的最大公约数

invert(x, y):求x模y的逆元,invert()方法在gmpy2下,(from gmpy2 import invert)

ord():用于返回一个字符对应的ASCII 数值或Unicode 数值。

实现过程

1、随机生成一个素数

这里选择利用埃氏筛来生成1-10000以内的素数序列

埃氏筛

(此处定义摘自维基百科)埃氏筛:是一种用来生成的筛法,得名于古希腊数学家埃拉托斯特尼。其基本步骤是从最小的素数2开始,将该素数的所有倍数标记成合数,而下一个尚未被标记的最小自然数3即是下一个素数。如此重复这一过程,将各个素数的倍数标记为合数并找出下一个素数,最终便可找出一定范围内所有素数。

优化算法(下述推论在此不进行数学证明,直接使用)

1、寻找𝑁以内的素数时,若找到了一个大于根号N的素数,则剩余的所有尚未标记的数也都是素数。

2、标记某一素数𝑝的倍数时,不需要每次皆从2𝑝,3𝑝,…开始,而可直接从𝑝^2开始标记。

示例

如下图所示,使用埃氏筛法找出120以内的所有素数。由于11^2=121>120,当11成为最小的未标记整数时,尚未标记的所有数皆可确认为素数。

代码

prime_list = []
is_prime = [0 for i in range(m + 1)]
for i in range(2):
    is_prime[i] = 1
for n in range(2, m):
    if is_prime[n] == 0:
        if n <= math.sqrt(m):
            prime_list.append(n)
            for i in range(n, m // 2):
                if n * i <= m:
                    is_prime[n * i] = 1
                else:
                    break
        else:
            prime_list.append(n)
print(prime_list)

2、求整数e

随机在区间(1, \Phi(n))之间选择一个整数e, 使得gcd(e, \Phi(n)) = 1

下面第一段代码为我自己的思路,不如第二种方法简单,但还是放在这里,请大家指正(温馨提示:经我运行,此种求e方法太浪费时间,大家看看就好了)

x = 100
s = x
# 与x互质的数, 则其整数分解后的质因数一定与x互质
# 得到小于x且不是x因数的质数序列
x_list = primes_in_range(x)
del_list = []
for i in x_list:
    if x % i == 0:
        print(i)
        del_list.append(i)
        while x % i == 0:
            x = x / i
for i in del_list:
    x_list.remove(i)
# 在此序列中随机取最多3个数相乘, 来模拟区间(1, x)内与x互质的随机数
t = 1
i = 1
while i <= 3:
    z = random.choice(x_list)
    if t * z > s:
        break
    else:
        t *= z
    i += 1
print(t)

还有一种最简单粗暴的方法 

# 在区间(2, x)随机取数,直到取到数e与x互质
while 1:
    e = random.randrange(2, x)
    if math.gcd(e, x) == 1:
        print(e)

3、模逆运算

扩展欧几里得算法

(此方法详解不在此提及,感兴趣的可以见主页)

# 模逆运算
def gcd(a, b):
    while b:
        a, b = b, a % b
    return a


def stein(a, m):
    assert a > 0 and m > 1, "a must be positive and m must be greater than 1"
    if a == 1:
        return 1
    g = gcd(a, m)
    if g != 1:
        if (m // g) % 2 == 0:
            return -1
        else:
            return pow(a // g, (m // g) - 1, m)
    u1, u2, u3 = 1, 0, a
    v1, v2, v3 = 0, 1, m
    while v3 != 0:
        q = u3 // v3
        v1, v2, v3, u1, u2, u3 = (u1 - q * v1), (u2 - q * v2), (u3 - q * v3), v1, v2, v3
    if u1 < 0:
        u1 += m
    return u1

扩展Stein算法 

def stein(v, u):
    # 若不满足a>0且m>1, 则报错AssertionError
    assert v > 0 and u > 1, "a must be positive and m must be greater than 1"
    # 特例:1模任何数的逆元均为1
    if v == 1:
        return 1
    x1, x2, x3 = 1, 0, v
    y1, y2, y3 = 0, 1, u
    while x3 != 1 or y3 != 1:
        if x3 % 2 == 0:
            if x1 % 2 == 0 and x2 % 2 == 0:
                x1, x2, x3 = x1 / 2, x2 / 2, x3 / 2
            else:
                x1 = x1 + u
                x2 = x2 - v
        elif y3 % 2 == 0:
            if y1 % 2 == 0 and y2 % 2 == 0:
                y1, y2, y3 = y1 / 2, y2 / 2, y3 / 2
            else:
                y1 = y1 + u
                y2 = y2 - v
        else:
            if x3 > y3:
                x1, x2, x3 = x1 - y1, x2 - y2, x3 - y3
            else:
                y1, y2, y3 = y1 - x1, y2 - x2, y3 - x3
    if x3 == 1:
        return x1
    else:
        return y1

调用现成的函数

(在最终代码中,选择了简洁的调用库,但与上述的stenin算法是等价的,大家根据爱好选择)

# 引入此库
from gmpy2 import invert

# d即为x模y的逆元
d = invert(x, y)

整体代码

在下述代码中实现每次随机生成一对公私钥对,使用者只需记下公私钥对即可实现RSA加解密 

(插个题外话,望大家编写代码时仔细一些,某倒霉人误在调用decrypt()函数时将d输为e,检查了一晚上bug)

import random
import math
from gmpy2 import invert


# 生成m以内的素数序列
def primes_in_range(m):
    prime_list = []
    is_prime = [0 for i in range(m + 1)]
    for i in range(2):
        is_prime[i] = 1
    for n in range(2, m):
        if is_prime[n] == 0:
            if n <= math.sqrt(m):
                prime_list.append(n)
                for i in range(n, m // 2):
                    if n * i <= m:
                        is_prime[n * i] = 1
                    else:
                        break
            else:
                prime_list.append(n)
    return prime_list


# 生成整数e
def create_e(x):
    # 在区间(2, x)内随机取数,直到该数与e互质
    while 1:
        e = random.randrange(2, x)
        if math.gcd(e, x) == 1:
            return e


# 用公钥(e,n)将明文m进行加密,这里将每个字符存分别存在list列表里面
def encrypt(m, e, n):
    # 将明文的每一字符转换为其对应的ASCII码或Unicode数值, 再进行RSA加密后返回
    return [pow(ord(x), e, n) for x in m]


# 用私钥(d,n)将密文cipher进行解密
def decrypt(cipher, d, n):
    # 将密文对应的每一个数字进行RSA解密后, 转换为对应的字符, 加入到密文串
    return ''.join([chr(pow(x, d, n)) for x in cipher])


if __name__ == '__main__':
    prime = primes_in_range(10000)
    # 得到两个素数p,q
    p = random.choice(prime)
    while 1:
        q = random.choice(prime)
        if p != q:
            break
    # n, euler_n
    n = p * q
    euler_n = (p - 1) * (q - 1)
    e = create_e(euler_n)
    d = invert(e, euler_n)
    print("此次RSA的随机数p = {}, q = {}, e = {}".format(p, q, e))
    print("可求得公钥(n, e) = ({}, {}), 私钥d = {}".format(n, e, d))
    # 测试, 输入明文
    plainText = input('请输入测试明文:')
    # 求密文, plainText^e % n
    cipher = encrypt(plainText, e, n)
    print('加密结果=', cipher)
    # 求明文, C^d % n
    M = decrypt(cipher, d, n)
    print('解密结果=', M)

参考:

在 Python 中生成随机质数 | D栈 - Delft Stack

欧拉函数(详细证明+推导) 每日一遍,算法再见!-CSDN博客

Python——实现密码学中的模逆运算_python求模逆-CSDN博客

python求模逆元_python 求模逆-CSDN博客

Python实现RSA加解密_python实现rsa加解密类-CSDN博客

  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值