RSA加密算法及其证明 大数RSA问题解决方案

在这里插入图片描述

当我们给定公钥e与n时,对于很大的n,其欧拉函数的值是很难求解的(只有采取质因数分解的方法,而大数的质因数分解几乎是计算上不可行的)。
因此便很难求出私钥。

如何使用RSA加密
那么接收方Bob,通过选取合适的p和q,那么通过RSA算法,可以获得公钥和私钥,那么将公钥公布出去,私钥自己保存,那么别人就可以向接收方Bob发送密文,而只有拥有密钥的Bob才可以解密。

n为大数(1024bits)的RSA加密的python实现

面临的问题有:
1.如何创建两个不相等且足够大的素数:使用费马素性检验
2.如何求得e对于模数为n欧拉函数的乘法逆元:扩展欧几里得算法exgcd(a,b)求得模逆元d
3.对于加密解密,可以使用蒙哥马利算法 montgomery(n, p, m):快速计算(n^p)%m的值,即逐次平方法
值得注意的是:RSA加解密算法本身是操作于整数的模幂运算,而要加密的消息明文通常以字节序列表示,所以需要两个转换函数。

1.创建两个不相等且足够大的素数

先引进费马定理:
在这里插入图片描述
在这里插入图片描述

# 费马算法进行素数测试
test_times = 20


def isPrime(n):
    for i in range(0, test_times):
        a = random.randint(1, n - 1)
        if montgomery(a, n - 1, n) != 1:
            return False
    return True

快速指数取模的实现算法

逐次平方法:
在这里插入图片描述
在这里插入图片描述

# 蒙哥马利算法
# 快速计算(n^p)%m的值,即逐次平方法
def montgomery(n, p, m):
    k = 1
    n %= m #如果n过大可以先与m模一下
    while p != 1:
        if 0 != (p & 1):#使用与运算(p & 1)来判断是否二进制末尾为1,也就是是否为奇数
            k = (k * n) % m
        n = (n * n) % m
        p >>= 1
    return (k * n) % m

求得e对于模数为n欧拉函数的乘法逆元:扩展欧几里得算法exgcd(a,b)求得模逆元d

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

扩展欧几里得具体例子:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

# 计算方程 gcd(a,b)=ax+by 的其中一个解
# 返回值是一个元祖,分别为最大公约数gcd(a,b),以及一组x,y的解
def exgcd(a, b):
    x0, y0 = 1, 0
    x1, y1 = 0, 1
    x, y = 0, 1
    r = a % b#余数
    q = (a - r) // b#商
    while r != 0:#余数不等于0
        x = x0 - q * x1
        y = y0 - q * y1
        x0, y0 = x1, y1
        x1, y1 = x, y
        a = b#除数赋给被除数
        b = r#余数赋给除数
        r = a % b
        q = (a - r) // b
    return (b, x, y)

整个代码:

ZRSA

import random
# 根据指定的bits位数,生成
# 返回一个元组,分别为公钥和密钥
def generateKey(bits):
    p, q = _generatePQ(bits)
    n, e, d = _calcNED(p, q)
    return ((e, n), (d, p, q))


# 生成两个不相等的随机大质数
# bits表示要生成的大质数的二进制位数
# 两个大质数以元祖形式返回
def _generatePQ(bits):
    bits >>= 1
    p = randomPrimeBits(bits)
    # 采用Miller_rabin算法进行素数判定
    q = randomPrimeBits(bits)
    while p == q:
        q = randomPrimeBits(bits)
    # print('random prime finished. %d, %d'%(p, q))
    return (p, q)


# 生成指定位数的随机大质数
def randomPrimeBits(bits):
    base1 = 1 << (bits - 1)  # 1 * 2^(bits - 1)
    base2 = (1 << bits) - 1  # 1 * 2^bits - 1
    i = random.randint(base1, base2)
   # print('Random range: %d ~ %d. Base: %d'%(base1, base2, i))
    if i % 2 == 0: i += 1  # 如果生成的随机数是偶数,那么加 1
    while True:
        i += 2
        if i > base2:
            i = base1
        if isPrime(i):
            return i
        # print(i, flush=True)


## 素数判断

# 费马算法进行素数测试
test_times = 20


def isPrime(n):
    for i in range(0, test_times):
        a = random.randint(1, n - 1)
        if montgomery(a, n - 1, n) != 1:
            return False
    return True


# 蒙哥马利算法
# 快速计算(n^p)%m的值,即逐次平方法
def montgomery(n, p, m):
    k = 1
    n %= m #如果n过大可以先与m模一下
    while p != 1:
        if 0 != (p & 1):#使用与运算(p & 1)来判断是否二进制末尾为1,也就是是否为奇数
            k = (k * n) % m
        n = (n * n) % m
        p >>= 1
    return (k * n) % m


# 根据两个大质数P,Q计算出N,E,D
# 以元祖形式返回(n, e, d)
def _calcNED(p, q):
    t = (p - 1) * (q - 1)
    n = p * q
    e, d = _calcED(t)
    return (n, e, d)


def _calcED(t):
    recommande = (65537, 3, 17)
    e = 1

    # 其实因为t是合数,所以e只要选择任意一个质数都会通过
    for i in recommande:
        if i < t:
            e = i
            break

    d = modular_linear_equation(e, 1, t)[0]
    return (e, d)  # 私钥中的较大,解密和签名操作较慢,默认
    # return (d, e)  #公钥中的较大,加密和验证签名操作较慢


# 同余方程 ax≡b (mod n) 对于未知数 x 有解,当且仅当 gcd(a,n) | b。
# 且方程有解时,方程有 gcd(a,n) 个解
def modular_linear_equation(a, b, n):
    r = []
    d, x, y = exgcd(a, n)
    if b % d != 0:
        return r

    x0 = x * (b // d) % n  # 特解
    r.append(x0)
    for i in range(1, d):  # 剩余解
        r.append((x0 + i * (n - d)) % n)
    return r


# 计算方程 gcd(a,b)=ax+by 的其中一个解
# 返回值是一个元祖,分别为最大公约数gcd(a,b),以及一组x,y的解
def exgcd(a, b):
    x0, y0 = 1, 0
    x1, y1 = 0, 1
    x, y = 0, 1
    r = a % b#余数
    q = (a - r) // b#商
    while r != 0:#余数不等于0
        x = x0 - q * x1
        y = y0 - q * y1
        x0, y0 = x1, y1
        x1, y1 = x, y
        a = b#除数赋给被除数
        b = r#余数赋给除数
        r = a % b
        q = (a - r) // b
    return (b, x, y)


def rsaBits(key):
    n = 0
    if len(key) == 2: n = key[1]
    if len(key) == 3: n = key[1] * key[2]
    if n == 0: raise ValueError('key is not either public key nor private key')
    return _integerBits(n)


# 获取一个整数至少为多少位的整数
def _integerBits(n):
    count = 0
    while n != 0:
        count += 1
        n >>= 1

    return count
    # return n.bit_length()  #这个是python内置的函数,结果与我自己写的相同


# 因为明文不一定刚好能被等分完毕。所以开头用n个字节标记
# 剩下那一段的长度的。
codeTitle = b"Rsa:"
codeLeftLen = 4


def encrypt(data, pubkey):
    if len(pubkey) != 2: raise ValueError('pubkey error.')
    n = pubkey[1]
    if isinstance(data, type(b'')):  # Isinstance的用法是用来判断一个量是否是相应的类型

        # 加密时,对于每个分段,密文比明文多1字节
        srcSectionLen = (_integerBits(n) - 1) >> 3

        tgtSectionLen = srcSectionLen + 1

        # 求出分段数和多余字节数base
        lenData = len(data)
        base = lenData % srcSectionLen
        # print(base)
        ret = bytearray()
        # print(ret)
        # 加密第一段,仅当明文不能被分成整数份时
        if base > 0:
            a = int.from_bytes(data[0:base], 'big')  # byte类型转换为int类型函数
            # print('a:%d'%a)
            b = montgomery(a, pubkey[0], n)
            # print('b:%d'%b)

            # 蒙哥马利算法 montgomery(n, p, m):
            # 快速计算(n^p)%m的值,即逐次平方法
            ret.extend(codeTitle)
            # 要转化成bytes的数字num,要转化成的bytes的长度length,以字节为单位
            # def _intToBytes(num, length):
            #     return num.to_bytes(length, 'big', signed=False)
            ret.extend(_intToBytes(base, codeLeftLen))
            ret.extend(_intToBytes(b, tgtSectionLen))  # 写入第一段密文

        for i in range(base, lenData, srcSectionLen):
            sectionData = data[i: i + srcSectionLen]
            a = int.from_bytes(sectionData, 'big')
            b = montgomery(a, pubkey[0], n)
            ret.extend(_intToBytes(b, tgtSectionLen))
        # print(ret)
        return bytes(ret)
    else:
        raise TypeError("data must be 'bytes' type")


def _intToBytes(num, length):
    return num.to_bytes(length, 'big', signed=False)


def decrypt(data, prikey):
    if len(prikey) != 3: raise ValueError('prikey error.')
    n = prikey[1] * prikey[2]
    if isinstance(data, type(b'')):

        # 解密时,对于每个分段,密文比明文多1字节
        tgtSectionLen = (_integerBits(n) - 1) >> 3
        srcSectionLen = tgtSectionLen + 1

        # 求出分段数和多余字节数base
        lenData = len(data)
        ret = bytearray()

        # 解密第一段没有完整长度的密文
        pos = 0
        if data[0:len(codeTitle)] == codeTitle:
            pos = len(codeTitle)
            lena = int.from_bytes(data[pos:pos + codeLeftLen], 'big')
            pos += codeLeftLen
            a = int.from_bytes(data[pos:pos + srcSectionLen], 'big')
            pos += srcSectionLen
            b = montgomery(a, prikey[0], n)
            ret.extend(_intToBytes(b, lena))  # 写入第一段解密后的明文

        for i in range(pos, lenData, srcSectionLen):
            sectionData = data[i: i + srcSectionLen]
            a = int.from_bytes(sectionData, 'big')
            b = montgomery(a, prikey[0], n)
            ret.extend(_intToBytes(b, tgtSectionLen))

        return bytes(ret)
    else:
        raise TypeError("data must be 'bytes' type")

import ZRSA

if __name__ == '__main__':
    bits = 1024
    pubKey, priKey = ZRSA.generateKey(bits)  # 返回两个元组 公钥(e,n) 私钥#(d,p,q)
    # 1.首先生成两个随机的大质数:   _generatePQ返回p、q
    # 2.计算n、欧拉函数t、公钥、私钥
    #    欧拉函数t = (p - 1) * (q - 1)
    #           n = p * q
    #           recommande = (65537, 3, 17)三者生成一个公钥e
    #           modular_linear_equation(e, 1, t) 计算同余方程 ed≡1 (mod t) 生成私钥


    # pubfmt = '%x,%x'#输出为十六进制
    # prifmt = '%x,%x,%x'
    actBits = ZRSA.rsaBits(pubKey)  # 计算n的位数
    # print("n的位数为:")
    # print(actBits)
    print('---------------------计算结果如下:-----------------------------------------')
    print('公钥e:十六进制为%x   十进制为%d' % (pubKey[0], pubKey[0]))  # (e,n)
    print('私钥d:十六进制为%x   十进制为%d' % (priKey[0], priKey[0]))  # (d,p,q)
    print('n的实际位数: %s' % actBits)
    # print('公钥:' + pubfmt%pubKey)
    # print('密钥:' + prifmt%priKey)
    print('--------------------------------------------------------------------------')

    # # 输入原文M
    # #hex()函数用于将10进制整数转换成16进制,以字符串形式表示
    # #0001002304050607080910111213为转化好的16进制
    # M = ZRSA.hexStrToBytes('0001002304050607080910111213')
    # # print(M)    b'\x00\x01\x00#\x04\x05\x06\x07\x08\t\x10\x11\x12\x13'
    # MD = ZRSA.hexStrToBytes('0001020304050607080910111212')
    # print('原文:(%d bytes)\n%s'%(len(M),M))
    M = b'hjhhhhhhhhhhhhhhhhjhj'
    print(M)
    C = ZRSA.encrypt(M, pubKey)  # M只要为比特就行
    # b'Textbook RSA in Python'


    print('密文:(%d bytes)\n%s' % (len(C), C))


    m = ZRSA.decrypt(C, priKey)


    print('解密:(%d bytes)\n%s' % (len(m), m))


部分内容参考:https://www.packetmania.net/2021/03/01/Python-Textbook-RSA/#%E5%B7%A5%E5%85%B7%E5%87%BD%E6%95%B0

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值