• 25行代码实现完整的RSA算法(Python3.X版本)


    摘自:https://blog.csdn.net/bian_h_f612701198412/article/details/87202462


      python2.7版本的请点击这里25行代码实现完整的RSA算法
      网络上很多关于RSA算法的原理介绍,但是翻来翻去就是没有一个靠谱、让人信服的算法代码实现,即使有代码介绍,也都是直接调用JDK或者Python代码包中的API实现,也有可能并没有把核心放在原理的实现上,而是字符串转数字啦、或者数字转字符串啦、或者即使有代码也都写得特别烂。无形中让人感觉RSA加密算法竟然这么高深,然后就看不下去了。看到了这样的代码我就特别生气,四个字:误人子弟。还有我发现对于“大整数的幂次乘方取模”竟然采用直接计算的幂次的值,再取模,类似于(2 ^ 1024) ^ (2 ^ 1024),这样的计算就直接去计算了,我不知道各位博主有没有运行他们的代码???知道这个数字有多大吗?这么说吧,把全宇宙中的物质都做成硬盘都放不下,更何况你的512M内存的电脑。所以我说他们的代码只可远观而不可亵玩已。
      于是我用了2天时间,没有去参考网上的代码重新开始把RSA算法的代码完全实现了一遍以后发现代码竟然这么少,基本上25行就全部搞定。为了方便整数的计算,我使用了Python语言。为什么用Python?因为Python在数值计算上比较直观,即使没有学习过python的人,也能一眼就看懂了代码。而Java语言需要用到BigInteger类,数值的计算都是用方法调用,所以使用起来比较麻烦。如果有同学对我得代码感兴趣的话,先二话不说,不管3X7=22,把代码粘贴进pydev中运行一遍,是驴是马拉出来溜溜。看不懂可以私信我,我就把代码具体讲讲,如果本文章没有人感兴趣,我就不做讲解了。
      RSA算法的步骤主要有以下几个步骤:
        1、选择 p、q两个超级大的质数 ,都是1024位,显得咱们的程序货真价实。
        2、令n = p * q。取 φ(n) =(p-1) * (q-1)。 计算与n互质的整数的个数。
        3、取 e ∈ 1 < e < φ(n) ,( n , e )作为公钥对,正式环境中取65537。可以打开任意一个被认证过的https证书,都可以看到。
        4、令 ed mod φ(n) = 1,计算d,( n , d ) 作为私钥对。 计算d可以利用扩展欧几里的算法进行计算,非常简单,不超过5行代码就搞定。
        5、销毁 p、q。密文 = 明文 ^ e mod n , 明文 = 密文 ^ d mod n。利用蒙哥马利方法进行计算,也叫反复平方法,非常简单,不超过10行代码搞定。
        实测:秘钥长度在2048位的时候,我的thinkpad笔记本T440上面、python3.6.3环境的运行时间是0.038秒,1024位的时候是0.007秒。说明了RSA加密算法的算法复杂度应该是O(N^2),其中n是秘钥长度。不知道能不能优化到O(NlogN)。其实该程序还有很大的理论优化空间。
      代码主要涉及到三个Python可执行文件:计算最大公约数、大整数幂取模算法、公钥私钥生成及加解密。这三个文件构成了RSA算法的核心。
      这个时候很多同学就不干了,说为什么我在网上看到的很多RSA理论都特别多,都分很多个章节,在每个章节中,都有好多个屏幕才能显示完,这么多的理论,想想怎么也得上千行代码才能实现,怎么到了你这里25行就搞定了呢?北门大官人你不会是在糊弄我们把?其实真的没有,我是良心博主,绝对不会糊弄大家,你们看到的理论确实这么多,我也都看过了,我把这些理论用了zip,gzip,hafuman,tar,rar等很多的压缩算法一遍遍地进行压缩,才有了这个微缩版的rsa代码实现,代码虽少,五脏俱全,是你居家旅行,课程设计、忽悠小白、必备良药。其实里边的几乎每一行代码都能写一篇博客专门进行介绍。
      前方高能,我要开始装逼了。看不懂的童鞋请绕道,先去看看理论,具体内容如下:
      1. 计算最大公约数
      2. 超大整数的超大整数次幂取超大整数模算法(好拗口,哈哈,不拗口一点就显示不出这个算法的超级牛逼之处)
      3. 公钥私钥生成

    1、计算最大公约数与扩展欧几里得算法

      ***gcd.py***文件,gcd方法用来计算两个整数的最大公约数。ext_gcd是扩展欧几里得方法的计算公式。

    # -*- coding: utf-8 -*-
    
    # 求两个数字的最大公约数(欧几里得算法)
    def gcd(a, b):
        if b == 0:
            return a
        else:
            return gcd(b, a % b)
    
    '''
    扩展欧几里的算法
    计算 ax + by = 1中的x与y的整数解(a与b互质)
    '''
    def ext_gcd(a, b):
        if b == 0:
            x1 = 1
            y1 = 0
            x = x1
            y = y1
            r = a
            return r, x, y
        else:
            r, x1, y1 = ext_gcd(b, a % b)
            x = y1
            y = x1 - a // b * y1
            return r, x, y

    2、大整数幂取模算法

      ***exponentiation.py***文件,主要用于计算超大整数超大次幂然后对超大的整数取模。我在网上查询到这个算法叫做“蒙哥马利算法”。也叫反复平方法。非常简单,具体算法详情请参考这里蒙哥马利算法

    # -*- coding: utf-8 -*-
    
    '''
    超大整数超大次幂然后对超大的整数取模
    (base ^ exponent) mod n
    '''
    import time
    def exp_mode(base, exponent, n):
        bin_array = bin(exponent)[2:][::-1]
        r = len(bin_array)
        base_array = []
        
        pre_base = base
        base_array.append(pre_base)
        
        for _ in range(r - 1):
            next_base = (pre_base * pre_base) % n 
            base_array.append(next_base)
            pre_base = next_base
            
        a_w_b = __multi(base_array, bin_array, n)
        return a_w_b % n
    
    def __multi(array, bin_array, n):
        result = 1
        for index in range(len(array)):
            a = array[index]
            if not int(bin_array[index]):
                continue
            result *= a
            result = result % n # 加快连乘的速度
        return result

    有同学就不服了,说是我为啥不把这个幂次的数字计算出来,再取模。我说这样做,理论上是对的,但是实际上行不通。因为:一个2048位的数字的2048位次的幂,计算出来了以后,这个数字很可能把全宇宙的物质都做成硬盘也放不下。不懂的童鞋请私信我。所以需要用“蒙哥马利算法”进行优化。

    3、公钥私钥生成

    rsa.py,生成公钥、私钥、并对信息加密解密。
      咱是实在博主,绝对不会弄虚作假,在p和q的选择上,今年过年不选p、q,要选就选1024位。很多博客中在选取p和q的时候都是使用10000以内的质数,象征性地给大家演示一下,把问题说明白,结果在计算的时候就偷懒了,直接把幂次计算出来。这个明显偷懒了,没有把问题说明白。

    # -*- coding: utf-8 -*-
    from gcd import ext_gcd
    from exponentiation import exp_mode
    import time
    
    # 生成公钥私钥,p、q为两个超大质数
    def gen_key(p, q):
        n = p * q
        fy = (p - 1) * (q - 1)      # 计算与n互质的整数个数 欧拉函数
        e = 65537                    # 选取e   一般选取65537
        # generate d
        a = e
        b = fy
        r, x, y = ext_gcd(a, b)
        # 计算出的x不能是负数,如果是负数,说明p、q、e选取失败,不过可以把x加上fy,使x为正数,才能计算。
        if x < 0:
            x = x + fy
        d = x
        # 返回:   公钥     私钥
        return    (n, e), (n, d)
        
    # 加密 m是被加密的信息 加密成为c
    def encrypt(m, pubkey):
        n = pubkey[0]
        e = pubkey[1]
        
        c = exp_mode(m, e, n)
        return c
    
    # 解密 c是密文,解密为明文m
    def decrypt(c, selfkey):
        n = selfkey[0]
        d = selfkey[1]
        
        m = exp_mode(c, d, n)
        return m
        
        
    if __name__ == "__main__":
        '''公钥私钥中用到的两个大质数p,q,都是1024位'''
        p = 106697219132480173106064317148705638676529121742557567770857687729397446898790451577487723991083173010242416863238099716044775658681981821407922722052778958942891831033512463262741053961681512908218003840408526915629689432111480588966800949428079015682624591636010678691927285321708935076221951173426894836169
        q = 144819424465842307806353672547344125290716753535239658417883828941232509622838692761917211806963011168822281666033695157426515864265527046213326145174398018859056439431422867957079149967592078894410082695714160599647180947207504108618794637872261572262805565517756922288320779308895819726074229154002310375209
        '''生成公钥私钥'''
        pubkey, selfkey = gen_key(p, q)
        '''需要被加密的信息转化成数字,长度小于秘钥n的长度,如果信息长度大于n的长度,那么分段进行加密,分段解密即可。'''
        m = 1356205320457610288745198967657644166379972189839804389074591563666634066646564410685955217825048626066190866536592405966964024022236587593447122392540038493893121248948780525117822889230574978651418075403357439692743398250207060920929117606033490559159560987768768324823011579283223392964454439904542675637683985296529882973798752471233683249209762843835985174607047556306705224118165162905676610067022517682197138138621344578050034245933990790845007906416093198845798901781830868021761765904777531676765131379495584915533823288125255520904108500256867069512326595285549579378834222350197662163243932424184772115345
        print("待加密信息-->%s" % m)
        '''信息加密,m被加密的信息,c是加密后的信息'''
        c = encrypt(m, pubkey)
        print("被加密后的密文-->%s" % c)
        '''信息解密'''
        d = decrypt(c, selfkey)
        print("被解密后的明文-->%s" % d)

    代码就是这么简单,RSA算法就是这么任性。代码去除掉没用的注释或者引用,总长度不会超过25行,有疑问的我们掰扯掰扯。
      实测:秘钥长度在2048位的时候,我的thinkpad笔记本T440上面、python3.6.3环境的运行时间是0.038秒,1024位的时候是0.007秒。说明了RSA加密算法的算法复杂度应该是O(N^2),其中n是秘钥长度。不知道能不能优化到O(NlogN),如果想看python2.7版本的,请点击这里

  • 相关阅读:
    fenby C语言 P32
    fenby C语言 P31 使用数组的指针
    fenby C语言 P30
    fenby C语言 P29
    fenby C语言 P28
    fenby C语言 P27使用指针
    fenby C语言 P25
    fenby C语言 P26
    fenby C语言P24
    Python学习之路:通过socket实现处理多个连接
  • 原文地址:https://www.cnblogs.com/LiuYanYGZ/p/12522303.html
Copyright © 2020-2023  润新知