Encrypt & Decrypt using PyCrypto AES 256

后端 未结 12 890
夕颜
夕颜 2020-11-22 13:07

I\'m trying to build two functions using PyCrypto that accept two parameters: the message and the key, and then encrypt/decrypt the message.

I found several links on

相关标签:
12条回答
  • 2020-11-22 13:17

    https://stackoverflow.com/a/21928790/11402877

    compatible utf-8 encoding

    def _pad(self, s):
        s = s.encode()
        res = s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs).encode()
        return res
    
    0 讨论(0)
  • 2020-11-22 13:20

    You may need the following two functions: pad- to pad(when doing encryption) and unpad- to unpad (when doing decryption) when the length of input is not a multiple of BLOCK_SIZE.

    BS = 16
    pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) 
    unpad = lambda s : s[:-ord(s[len(s)-1:])]
    

    So you're asking the length of key? You can use the md5sum of the key rather than use it directly.

    More, according to my little experience of using PyCrypto, the IV is used to mix up the output of a encryption when input is same, so the IV is chosen as a random string, and use it as part of the encryption output, and then use it to decrypt the message.

    And here's my implementation, hope it will be useful for you:

    import base64
    from Crypto.Cipher import AES
    from Crypto import Random
    
    class AESCipher:
        def __init__( self, key ):
            self.key = key
    
        def encrypt( self, raw ):
            raw = pad(raw)
            iv = Random.new().read( AES.block_size )
            cipher = AES.new( self.key, AES.MODE_CBC, iv )
            return base64.b64encode( iv + cipher.encrypt( raw ) ) 
    
        def decrypt( self, enc ):
            enc = base64.b64decode(enc)
            iv = enc[:16]
            cipher = AES.new(self.key, AES.MODE_CBC, iv )
            return unpad(cipher.decrypt( enc[16:] ))
    
    0 讨论(0)
  • 2020-11-22 13:26

    For the benefit of others, here is my decryption implementation which I got to by combining the answers of @Cyril and @Marcus. This assumes that this coming in via HTTP Request with the encryptedText quoted and base64 encoded.

    import base64
    import urllib2
    from Crypto.Cipher import AES
    
    
    def decrypt(quotedEncodedEncrypted):
        key = 'SecretKey'
    
        encodedEncrypted = urllib2.unquote(quotedEncodedEncrypted)
    
        cipher = AES.new(key)
        decrypted = cipher.decrypt(base64.b64decode(encodedEncrypted))[:16]
    
        for i in range(1, len(base64.b64decode(encodedEncrypted))/16):
            cipher = AES.new(key, AES.MODE_CBC, base64.b64decode(encodedEncrypted)[(i-1)*16:i*16])
            decrypted += cipher.decrypt(base64.b64decode(encodedEncrypted)[i*16:])[:16]
    
        return decrypted.strip()
    
    0 讨论(0)
  • 2020-11-22 13:27

    I have used both Crypto and PyCryptodomex library and it is blazing fast...

    import base64
    import hashlib
    from Cryptodome.Cipher import AES as domeAES
    from Cryptodome.Random import get_random_bytes
    from Crypto import Random
    from Crypto.Cipher import AES as cryptoAES
    
    BLOCK_SIZE = AES.block_size
    
    key = "my_secret_key".encode()
    __key__ = hashlib.sha256(key).digest()
    print(__key__)
    
    def encrypt(raw):
        BS = cryptoAES.block_size
        pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
        raw = base64.b64encode(pad(raw).encode('utf8'))
        iv = get_random_bytes(cryptoAES.block_size)
        cipher = cryptoAES.new(key= __key__, mode= cryptoAES.MODE_CFB,iv= iv)
        a= base64.b64encode(iv + cipher.encrypt(raw))
        IV = Random.new().read(BLOCK_SIZE)
        aes = domeAES.new(__key__, domeAES.MODE_CFB, IV)
        b = base64.b64encode(IV + aes.encrypt(a))
        return b
    
    def decrypt(enc):
        passphrase = __key__
        encrypted = base64.b64decode(enc)
        IV = encrypted[:BLOCK_SIZE]
        aes = domeAES.new(passphrase, domeAES.MODE_CFB, IV)
        enc = aes.decrypt(encrypted[BLOCK_SIZE:])
        unpad = lambda s: s[:-ord(s[-1:])]
        enc = base64.b64decode(enc)
        iv = enc[:cryptoAES.block_size]
        cipher = cryptoAES.new(__key__, cryptoAES.MODE_CFB, iv)
        b=  unpad(base64.b64decode(cipher.decrypt(enc[cryptoAES.block_size:])).decode('utf8'))
        return b
    
    encrypted_data =encrypt("Hi Steven!!!!!")
    print(encrypted_data)
    print("=======")
    decrypted_data = decrypt(encrypted_data)
    print(decrypted_data)
    
    0 讨论(0)
  • 2020-11-22 13:28

    You can get a passphrase out of an arbitrary password by using a cryptographic hash function (NOT Python's builtin hash) like SHA-1 or SHA-256. Python includes support for both in its standard library:

    import hashlib
    
    hashlib.sha1("this is my awesome password").digest() # => a 20 byte string
    hashlib.sha256("another awesome password").digest() # => a 32 byte string
    

    You can truncate a cryptographic hash value just by using [:16] or [:24] and it will retain its security up to the length you specify.

    0 讨论(0)
  • 2020-11-22 13:31

    Let me address your question about "modes." AES256 is a kind of block cipher. It takes as input a 32-byte key and a 16-byte string, called the block and outputs a block. We use AES in a mode of operation in order to encrypt. The solutions above suggest using CBC, which is one example. Another is called CTR, and it's somewhat easier to use:

    from Crypto.Cipher import AES
    from Crypto.Util import Counter
    from Crypto import Random
    
    # AES supports multiple key sizes: 16 (AES128), 24 (AES192), or 32 (AES256).
    key_bytes = 32
    
    # Takes as input a 32-byte key and an arbitrary-length plaintext and returns a
    # pair (iv, ciphtertext). "iv" stands for initialization vector.
    def encrypt(key, plaintext):
        assert len(key) == key_bytes
    
        # Choose a random, 16-byte IV.
        iv = Random.new().read(AES.block_size)
    
        # Convert the IV to a Python integer.
        iv_int = int(binascii.hexlify(iv), 16) 
    
        # Create a new Counter object with IV = iv_int.
        ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)
    
        # Create AES-CTR cipher.
        aes = AES.new(key, AES.MODE_CTR, counter=ctr)
    
        # Encrypt and return IV and ciphertext.
        ciphertext = aes.encrypt(plaintext)
        return (iv, ciphertext)
    
    # Takes as input a 32-byte key, a 16-byte IV, and a ciphertext, and outputs the
    # corresponding plaintext.
    def decrypt(key, iv, ciphertext):
        assert len(key) == key_bytes
    
        # Initialize counter for decryption. iv should be the same as the output of
        # encrypt().
        iv_int = int(iv.encode('hex'), 16) 
        ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)
    
        # Create AES-CTR cipher.
        aes = AES.new(key, AES.MODE_CTR, counter=ctr)
    
        # Decrypt and return the plaintext.
        plaintext = aes.decrypt(ciphertext)
        return plaintext
    
    (iv, ciphertext) = encrypt(key, 'hella')
    print decrypt(key, iv, ciphertext)
    

    This is often referred to as AES-CTR. I would advise caution in using AES-CBC with PyCrypto. The reason is that it requires you to specify the padding scheme, as exemplified by the other solutions given. In general, if you're not very careful about the padding, there are attacks that completely break encryption!

    Now, it's important to note that the key must be a random, 32-byte string; a password does not suffice. Normally, the key is generated like so:

    # Nominal way to generate a fresh key. This calls the system's random number
    # generator (RNG).
    key1 = Random.new().read(key_bytes)
    

    A key may be derived from a password, too:

    # It's also possible to derive a key from a password, but it's important that
    # the password have high entropy, meaning difficult to predict.
    password = "This is a rather weak password."
    
    # For added # security, we add a "salt", which increases the entropy.
    #
    # In this example, we use the same RNG to produce the salt that we used to
    # produce key1.
    salt_bytes = 8 
    salt = Random.new().read(salt_bytes)
    
    # Stands for "Password-based key derivation function 2"
    key2 = PBKDF2(password, salt, key_bytes)
    

    Some solutions above suggest using SHA256 for deriving the key, but this is generally considered bad cryptographic practice. Check out wikipedia for more on modes of operation.

    0 讨论(0)
提交回复
热议问题