• RSA公钥算法


     

     生成RSA公钥和私钥源代码:

    # RSA Key Generator
    # http://inventwithpython.com/hacking (BSD Licensed)
    
    import random, sys, os, rabinMiller, cryptomath
    
    
    def main():
        # create a public/private keypair with 1024 bit keys
        print('Making key files...')
        makeKeyFiles('My_al_sweigart', 1024)
        print('Key files made.')
    
    def generateKey(keySize):
        # Creates a public/private key pair with keys that are keySize bits in
        # size. This function may take a while to run.
    
        # Step 1: Create two prime numbers, p and q. Calculate n = p * q.
        print('Generating p prime...')
        p = rabinMiller.generateLargePrime(keySize)
        print('Generating q prime...')
        q = rabinMiller.generateLargePrime(keySize)
        n = p * q
    
        # Step 2: Create a number e that is relatively prime to (p-1)*(q-1).
        print('Generating e that is relatively prime to (p-1)*(q-1)...')
        while True:
            # Keep trying random numbers for e until one is valid.
            e = random.randrange(2 ** (keySize - 1), 2 ** (keySize))
            if cryptomath.gcd(e, (p - 1) * (q - 1)) == 1:
                break
    
        # Step 3: Calculate d, the mod inverse of e.
        print('Calculating d that is mod inverse of e...')
        d = cryptomath.findModInverse(e, (p - 1) * (q - 1))
    
        publicKey = (n, e)
        privateKey = (n, d)
    
        print('Public key:', publicKey)
        print('Private key:', privateKey)
    
        return (publicKey, privateKey)
    
    
    def makeKeyFiles(name, keySize):
        # Creates two files 'x_pubkey.txt' and 'x_privkey.txt' (where x is the
        # value in name) with the the n,e and d,e integers written in them,
        # delimited by a comma.
    
        # Our safety check will prevent us from overwriting our old key files:
        if os.path.exists('%s_pubkey.txt' % (name)) or os.path.exists('%s_privkey.txt' % (name)):
            sys.exit('WARNING: The file %s_pubkey.txt or %s_privkey.txt already exists! Use a different name or delete these files and re-run this program.' % (name, name))
    
        publicKey, privateKey = generateKey(keySize)
    
        print()
        print('The public key is a %s and a %s digit number.' % (len(str(publicKey[0])), len(str(publicKey[1]))))
        print('Writing public key to file %s_pubkey.txt...' % (name))
        fo = open('%s_pubkey.txt' % (name), 'w')
        fo.write('%s,%s,%s' % (keySize, publicKey[0], publicKey[1]))
        fo.close()
    
        print()
        print('The private key is a %s and a %s digit number.' % (len(str(publicKey[0])), len(str(publicKey[1]))))
        print('Writing private key to file %s_privkey.txt...' % (name))
        fo = open('%s_privkey.txt' % (name), 'w')
        fo.write('%s,%s,%s' % (keySize, privateKey[0], privateKey[1]))
        fo.close()
    
    
    # If makeRsaKeys.py is run (instead of imported as a module) call
    # the main() function.
    if __name__ == '__main__':
        main()

    输出:

    Making key files...
    Generating p prime...
    Generating q prime...
    Generating e that is relatively prime to (p-1)*(q-1)...
    Calculating d that is mod inverse of e...
    Public key: (20568209143252677078119058168035601492144407205238081134389623947553300520383618030722441824670851677422189702723379242411514807896505490593597921854779812062674476053726125420344205324094301464457020885689767121622218081530221054583979666503684425034850236608787337390539929601328218742032586632310678202364192746115447681548961879236126659198967246772560575411272270150281454124957024847454345144448492422499505523531063723013182118490082160576108092986312864068701483352352201348133176665929675070200383558450958684876765269246633232579765908427132470253986564733180661636578698481528251410808656000495485885064431, 177181900604485385859089278995336054349195227209474666610202818353053441867040090258520032166314961421663592461885970774558513073204274276330590792406481442045089473007396586708371196053951107573163533459940834178409635777108403047324843946055420755897292458294340939921932071421000515446556064026169271234081)      
    Private key: (20568209143252677078119058168035601492144407205238081134389623947553300520383618030722441824670851677422189702723379242411514807896505490593597921854779812062674476053726125420344205324094301464457020885689767121622218081530221054583979666503684425034850236608787337390539929601328218742032586632310678202364192746115447681548961879236126659198967246772560575411272270150281454124957024847454345144448492422499505523531063723013182118490082160576108092986312864068701483352352201348133176665929675070200383558450958684876765269246633232579765908427132470253986564733180661636578698481528251410808656000495485885064431, 7544297804112911499835595438087786244369293759362340426923724898724129142903334627532565872658504657521094004289475130282101086721496339841763432746059520461931838529840299232037775380093296293819701492727284536815962451803964299140089004238048627952122461961009066850182817277670646751222914605884206560142748298012655939192075865725846086784942443603911613591811516706031116496827201593383652693058605818812988727083044414499024334725416252854928022025717438652365028432440776312271001450594807555403586614201310624921975796215190494396608061113848000305749355551768849965813180689089873683173378913796765576813577)
    
    The public key is a 617 and a 309 digit number.
    Writing public key to file My_al_sweigart_pubkey.txt...
    
    The private key is a 617 and a 309 digit number.
    Writing private key to file My_al_sweigart_privkey.txt...
    Key files made.

    P.S.

    •   这个算法用到了RabinMiller算法生成质数
    •   用到了扩展欧几里得算法findModInverse来找到模逆
    •   生成文件代码

        os.path.exists('%s_pubkey.txt' % (name))检查文件name.txt是否存在

        fo = open('%s_pubkey.txt' % (name), 'w')
        fo.write('%s,%s,%s' % (keySize, publicKey[0], publicKey[1]))
        fo.close()

     注意,RSA加密算法是分组加密。每一组为一个(明文密文)块,而且,RSA只能加密文本文件,而不能加密二进制文件

    但是,所有的数据都是以数字的形式保存在计算机中的, 那么就需要一个字符到数字的映射:

    ASCII美国信息交换标准代码:

     一个ASCII字符使用一个字节的内存来存储。

    一个字节可以保存0 - 255 中的任意一个数共256个数
    1 byte = 8 bit
    所以ASCII实现了从字符和数字之间的转化且作为国际标准,使用与英语消息included in ASCII表中。
     

    chr() 和 ord():字符和数字之间的转化

    >>> chr(88) 
    'X'
    >>> chr(333) 
    'ō'
    >>> chr(3335) 
    ''
    >>> chr(33357) 
    ''
    >>> ?
      File "<stdin>", line 1
        ?
        ^
    SyntaxError: invalid syntax
    >>> ord('A') 
    65
    >>> chr(ord('A')) 
    'A'
    >>> ord('AW')     
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: ord() expected a character, but string of length 2 found
    >>>

    但是chr() 和 ord()只能是做到单个字符的转化。所以必须有一种字符串的转化。

    区块

    密码学中,“区块” 是 固定长度的位。 (p.s. 常有128字节  1024字节)

    在RSA加密中,要求区块大小等于或者小于密钥大小

    我们的区块如果是128个字节,那么它可以表示0到256^128(不含)的任何一个整数,可以想象,RSA中到处是大整数

    为什么要用区块,和大整数?

      如果使用单个字符的话,相同的消息会被加密成相同的结果,就有点像是仿射和凯撒加密法了。从安全性角度说,

      RSA的安全性是基于大整数分解成两个素数的困难

    现在问题就涉及了如何把字符串转化成一个大整数,即:

      把ASCII整数合并成一个大的数字!===========把字符串编码成区

    举个栗子:编码'Hello World!'

     

     这个最后的整数和就是密文的区块!!!

    因为我们的区块是设置了128个字节,所以在一个区块里最多只能加密128个字符。

    所以如果消息超过了128个字符,必须对其进行分组从而加密多个区块,RSA会用逗号‘,’来间隔不同的区块

    encode()字符串方法

    encode()字符串方法将返回一个"Bytes"对象,是普通字符串值前面加上一个'b'  b和后面的字符串之间没有空格

    >>> spam = 'hello'.encode('ascii')  
    >>> spam
    b'hello'
    >>> list(spam) 
    [104, 101, 108, 108, 111]
    >>> len(spam) 
    5
    >>> spam[2] 
    108
    >>>

    可以通过调用bytes()函数来创建"Bytes"对象

    >>> spam = bytes([123, 854, 25, 69, 255]) 
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: bytes must be in range(0, 256)
    >>> spam = bytes([123, 85, 25, 69, 255])  
    >>> spam
    b'{Ux19Exff'
    >>> spam = b'World' 
    >>> spam
    b'World'
    >>> list(spam) 
    [87, 111, 114, 108, 100]
    >>>

    decode()• bytes对象方法

    在一个"bytes"对象上调用decode()方法,返回这个bytes对象里保存的字符串

    >>> spam = bytes([104, 101, 108, 108, 111]) 
    >>> spam
    b'hello'
    >>> spam.decode('ascii') 
    'hello'
    >>>

    insert()列表方法

    insert()列表方法可以把一个值插入到列表中的任何一个地方,第一个参数是插入到的地方的索引,第二个参数是要插入的值

    >>> spam = [2, 5, 9, 99] 
    >>> spam.insert(0, 555) 
    >>> spam
    [555, 2, 5, 9, 99]
    >>> spam.insert(7, 777) 
    >>> spam
    [555, 2, 5, 9, 99, 777]
    >>> spam.insert(6, 7777777) 
    >>> spam
    [555, 2, 5, 9, 99, 777, 7777777]
    >>> spam.insert(0, 'dsdasd') 
    >>> spam
    ['dsdasd', 555, 2, 5, 9, 99, 777, 7777777]
    >>>

    加密源码:

    # RSA Cipher
    # http://inventwithpython.com/hacking (BSD Licensed)
    
    import sys
    
    # IMPORTANT: The block size MUST be less than or equal to the key size!
    # (Note: The block size is in bytes, the key size is in bits. There
    # are 8 bits in 1 byte.)
    # 
    DEFAULT_BLOCK_SIZE = 128 # 128 bytes
    # 一个字节可以保存0 - 255 中的任意一个数共256个数
    BYTE_SIZE = 256 # One byte has 256 different values.
    
    def main():
        # Runs a test that encrypts a message to a file or decrypts a message
        # from a file.
        filename = 'encrypted_file.txt' # the file to write to/read from
        mode = 'decrypt' # set to 'encrypt' or 'decrypt'
    
        if mode == 'encrypt':
            message = '''"Journalists belong in the gutter because that is where the ruling classes throw their guilty secrets." -Gerald Priestland "The Founding Fathers gave the free press the protection it must have to bare the secrets of government and inform the people." -Hugo Black'''
            pubKeyFilename = 'al_sweigart_pubkey.txt'
            print('Encrypting and writing to %s...' % (filename))
            encryptedText = encryptAndWriteToFile(filename, pubKeyFilename, message)
    
            print('Encrypted text:')
            print(encryptedText)
    
        elif mode == 'decrypt':
            privKeyFilename = 'al_sweigart_privkey.txt'
            print('Reading from %s and decrypting...' % (filename))
            decryptedText = readFromFileAndDecrypt(filename, privKeyFilename)
    
            print('Decrypted text:')
            print(decryptedText)
    
    # 把消息转成区块,很多个区块,
    def getBlocksFromText(message, blockSize=DEFAULT_BLOCK_SIZE):
        # Converts a string message to a list of block integers. Each integer
        # represents 128 (or whatever blockSize is set to) string characters.
    
        
        messageBytes = message.encode('ascii') # convert the string对象 to bytes对象
    
        # 每个区块转换成一个很大的整数,保存到blockInts中
        blockInts = []
        for blockStart in range(0, len(messageBytes), blockSize):  # 正在创建区块的索引 第一个区块索引为0,第一个区块索引为128(取决于blockSize), 第一个区块索引为256,   区块的索引小于len(messageBytes)
            # Calculate the block integer for this block of text
            blockInt = 0
            for i in range(blockStart, min(blockStart + blockSize, len(messageBytes))):    #因为最后一个区块可能是刚好128个字符,但通常会小于128个字符len(messageBytes)-1正是messageBytes的最后一个索引
                #blockInt是一个大整数  messageBytes[i] * (BYTE_SIZE ** (i % blockSize))是字符的sacii乘以256^(每个块中的字符索引)  
                blockInt += messageBytes[i] * (BYTE_SIZE ** (i % blockSize))    #BYTE_SIZE = 256  (i % blockSize)是0~字符串长度
            blockInts.append(blockInt)
        return blockInts
    
    # 把很多个区块转换成字符消息   必须要messageLength参数为了最后一个区块需要
    def getTextFromBlocks(blockInts, messageLength, blockSize=DEFAULT_BLOCK_SIZE):
        # Converts a list of block integers to the original message string.
        # The original message length is needed to properly convert the last
        # block integer.
        # 注意最后这是一个message字符列表  最后需要建立连接
        message = []
        for blockInt in blockInts:
            blockMessage = []
            # 从blockInt提取sacii数字的方式是反向进行的,从blockSize - 1开始,每个迭代-1,直到-1(不包括)
            for i in range(blockSize - 1, -1, -1):
                if len(message) + i < messageLength:
                    # Decode the message string for the 128 (or whatever
                    # blockSize is set to) characters from this block integer.
                    asciiNumber = blockInt // (BYTE_SIZE ** i)
                    # 既然我们已经计算了这个字符,就需要移除,通过blockInt除以(BYTE_SIZE ** i)取余得到,yeap   逆向嘛
                    blockInt = blockInt % (BYTE_SIZE ** i)
                    # 一个一个字符asciiNumber   0~255插入
                    blockMessage.insert(0, chr(asciiNumber))
            message.extend(blockMessage)
        return ''.join(message)
    
    
    def encryptMessage(message, key, blockSize=DEFAULT_BLOCK_SIZE):
        # Converts the message string into a list of block integers, and then
        # encrypts each block integer. Pass the PUBLIC key to encrypt.
        encryptedBlocks = []
        n, e = key
    
        for block in getBlocksFromText(message, blockSize):
            # ciphertext = plaintext ^ e mod n
            encryptedBlocks.append(pow(block, e, n))        # C = M^e mod n
        return encryptedBlocks
    
    
    def decryptMessage(encryptedBlocks, messageLength, key, blockSize=DEFAULT_BLOCK_SIZE):
        # Decrypts a list of encrypted block ints into the original message
        # string. The original message length is required to properly decrypt
        # the last block. Be sure to pass the PRIVATE key to decrypt.
        decryptedBlocks = []
        n, d = key
        for block in encryptedBlocks:
            # plaintext = ciphertext ^ d mod n
            decryptedBlocks.append(pow(block, d, n))        # M = C^d mod n
        return getTextFromBlocks(decryptedBlocks, messageLength, blockSize)
    
    
    def readKeyFile(keyFilename):
        # Given the filename of a file that contains a public or private key,
        # return the key as a (n,e) or (n,d) tuple value.
        fo = open(keyFilename)
        content = fo.read()
        fo.close()
        keySize, n, EorD = content.split(',')
        return (int(keySize), int(n), int(EorD))
    
    
    def encryptAndWriteToFile(messageFilename, keyFilename, message, blockSize=DEFAULT_BLOCK_SIZE):
        # Using a key from a key file, encrypt the message and save it to a
        # file. Returns the encrypted message string.
        keySize, n, e = readKeyFile(keyFilename)
    
        # Check that key size is greater than block size.
        if keySize < blockSize * 8: # * 8 to convert bytes to bits
            sys.exit('ERROR: Block size is %s bits and key size is %s bits. The RSA cipher requires the block size to be equal to or greater than the key size. Either decrease the block size or use different keys.' % (blockSize * 8, keySize))
    
    
        # Encrypt the message
        encryptedBlocks = encryptMessage(message, (n, e), blockSize)
    
        # Convert the large int values to one string value.
        for i in range(len(encryptedBlocks)):
            encryptedBlocks[i] = str(encryptedBlocks[i])
        encryptedContent = ','.join(encryptedBlocks)
    
        # Write out the encrypted string to the output file.
        encryptedContent = '%s_%s_%s' % (len(message), blockSize, encryptedContent)
        fo = open(messageFilename, 'w')
        fo.write(encryptedContent)
        fo.close()
        # Also return the encrypted string.
        return encryptedContent
    
    
    def readFromFileAndDecrypt(messageFilename, keyFilename):
        # Using a key from a key file, read an encrypted message from a file
        # and then decrypt it. Returns the decrypted message string.
        keySize, n, d = readKeyFile(keyFilename)
    
    
        # Read in the message length and the encrypted message from the file.
        fo = open(messageFilename)
        content = fo.read()
        messageLength, blockSize, encryptedMessage = content.split('_')
        messageLength = int(messageLength)
        blockSize = int(blockSize)
    
        # Check that key size is greater than block size.
        if keySize < blockSize * 8: # * 8 to convert bytes to bits
            sys.exit('ERROR: Block size is %s bits and key size is %s bits. The RSA cipher requires the block size to be equal to or greater than the key size. Did you specify the correct key file and encrypted file?' % (blockSize * 8, keySize))
    
        # Convert the encrypted message into large int values.
        encryptedBlocks = []
        for block in encryptedMessage.split(','):
            encryptedBlocks.append(int(block))
    
        # Decrypt the large int values.
        return decryptMessage(encryptedBlocks, messageLength, (n, d), blockSize)
    
    
    # If rsaCipher.py is run (instead of imported as a module) call
    # the main() function.
    if __name__ == '__main__':
        main()

    输出:

    加密 mode='encrypt'

    Encrypting and writing to encrypted_file.txt...
    Encrypted text:
    262_128_9926158891891412924886952141356136142542943862695072991250598006600270898300155338706636681856461575090075284572263362618218739769545313477249608401485234147843064609273929706353514554444810285427183303767133366827434264155196422091782649929928244535021903927052585385716925680743931745588143336997344189661596414349468058963048024948132923217849247276941269579027325396701709129191510084539012275457327046892059514600198713235394985023008043572425418307615110483262279656839322893000061931573893934153492056320331481641996204470201622784975235041470244964996075123464854629954207517620745550909143567815440815430367,6684261355384175628979536129678576912290902989264360857554803434400959272554726558432523319331127651229226379236001569105754247234449664301393066887072563919911914664504822721492217530056774346964092597494522555496959638903763181124233744530745204194891726109468870800424574799803024463576184984561160905385692143883155534327512132834866466005840402451465709012175029417109925035724824080741967623225446680099823178790059243202224297039960462494558200472899766913932921695002362188199621771371349477094464441789497029364384034674419241261434600801973782901186703144271104078294839144290043228508639879193883889311384,7277016624458973047704080668015657545528557043555314379029981553323365606133331342297139093317529026058177734586887567745897370142270546218412444852855142060252694055284415945350850536174716382559790627193026256934316461174349640238168693204610463496242533658473621140628689617878612045411645907503868803711923465905950382446525719000159190942639677572746105141288262702033570490198413350331921834181220670294175801373024013553583624428117568253845170657841567369678118510784447456725765265002966285445904361732332706663086388760638687504068870937711285114415078149377285832325922978358897651126143551277531003851780

    解密:mode='decrypt'

    Reading from encrypted_file.txt and decrypting...
    Decrypted text:
    "Journalists belong in the gutter because that is where the ruling classes throw their guilty secrets." -Gerald Priestland "The Founding Fathers gave the free press the protection it must have to bare the secrets of government and inform the people." -Hugo Black

    我们为什么不能破译RSA

    太多可能的密钥,暴力破解攻击NO

    密钥基于数字,字典攻击NO

    因为区块的引入,相同的明文加密成不同的东东西取决于区块在哪里,单词模式攻击NO

    单个加密区块表示多个字符,频率分析NO

    要解开一切并破译RSA,找出n的因数, n = p * q 再计算d,但这在数学上是困难的。

    完结撒花♠♣♥♦,继续努力✊✊✊✊✊✊✊✊

  • 相关阅读:
    IO流
    简单JSON
    开发流程
    命名规范
    策略模式
    Git的使用
    Markdown的使用
    代理模式
    装饰者模式
    POJ 2976 3111(二分-最大化平均值)
  • 原文地址:https://www.cnblogs.com/PiaYie/p/13513535.html
Copyright © 2020-2023  润新知