FRIHOST FORUMS SEARCH FAQ TOS BLOGS COMPETITIONS
You are invited to Log in or Register a free Frihost Account!

Encrypting and decrypting a file in Python




This program was written by Eli Bendersky and is described at: http://eli.thegreenplace.net/2010/06/25/aes-encryption-of-files-in-python-with-pycrypto/ . It was originally written for Python 2. I have modified it for Python 3. I try to keep things backward compatible when I can, but believe I am some distance away from that in this case. I am solely responsible for all the bugs I added. Thanks, Eli, for showing us how to use the pycrypto library (a little bit low-level for most of us who are not cryptographers).

Code:
#!/usr/bin/env python3

import sys, os, random, struct, hashlib
from Crypto.Cipher import AES

def hashkey(key, key_encoding):
    ''' do sha256 hash of the encryption key
    key: either a byte object [b'whatever'] or a string object
    '''
    hasher = hashlib.sha256() # create an object to compute the hash
    # todo: introspection is a lot less kludgy than this -- fix it
    if 'bytes' in str(type(key)):
        pass # no conversion. The key is presumably in the user's preferred encoding.
    elif 'str' in str(type(key)):
        key = bytes(key, key_encoding) # convert to bytes object
    hasher.update(key) # accepts either str or bytes as input to hashkey
    enckey = hasher.digest() # use the sha256 hash as the actual key
    del hasher
    return enckey


def encrypt_file(key, in_filename, out_filename=None,
                 chunksize=64*1024, key_encoding='ascii'):
    """ Encrypts a file using AES (CBC mode) with the
        given key.

        key:
            The encryption key - a string that will be padded to 32 bytes
            and encoded before use. The longer and more complex the key
            you specify, the more secure the encoding will be.
            Normally key is either a byte string or an ascii-only string.
            See key_encoding parameter for exceptions.

        in_filename:
            Name of the input file

        out_filename:
            If None, '<in_filename>.enc' will be used.

        chunksize (optional, a reasonable default is normally used):
            Sets the size of the chunk which the function
            uses to read and encrypt the file. Larger chunk
            sizes can be faster for some files and machines.
            chunksize must be divisible by 16.

        key_encoding (optional, normally not required):
            If the key is already a byte string, this will be ignored.
            If the key is a string containing only ascii characters,
            it will automatically be converted to a byte string. If there
            are non-ascii characters present, an appropriate encoding
            must be specified or an exception will be raised. The utf-8
            encoding is normally the safest choice if needed.
        """
    if not out_filename:
        out_filename = in_filename + '.enc'


    iv = ''.join(chr(random.randint(0, 0xFF)) for i in range(16))
    iv = str.encode(iv,'latin1')
    enckey = hashkey(key, key_encoding)
    #enckey = key + ( b' ' * (16-len(key)))
    encryptor = AES.new(enckey, AES.MODE_CBC, iv)
    filesize = os.path.getsize(in_filename)

    with open(in_filename, 'rb') as infile:
        with open(out_filename, 'wb') as outfile:
            outfile.write(struct.pack('<Q', filesize))
            outfile.write(iv)

            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                elif len(chunk) % 16 != 0:
                    chunk += b' ' * (16 - len(chunk) % 16)

                outfile.write(encryptor.encrypt(chunk))

def decrypt_file(key, in_filename, out_filename=None,
                 chunksize=24*1024, key_encoding='ascii'):
    """ Decrypts a file using AES (CBC mode) with the
        given key. Parameters are similar to encrypt_file,
        with one difference: out_filename, if not supplied
        will be in_filename without its last extension
        (i.e. if in_filename is 'aaa.zip.enc' then
        out_filename will be 'aaa.zip')
    """
    if not out_filename:
        out_filename = os.path.splitext(in_filename)[0]

    with open(in_filename, 'rb') as infile:
        origsize = struct.unpack('<Q', infile.read(struct.calcsize('Q')))[0]
        iv = infile.read(16)
        #enckey = key + ( b' ' * (16-len(key)))
        enckey = hashkey(key, key_encoding)

        decryptor = AES.new(enckey, AES.MODE_CBC, iv)

        with open(out_filename, 'wb') as outfile:
            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                outfile.write(decryptor.decrypt(chunk))

            outfile.truncate(origsize)

def main():
    key = 'kitty'
    encrypt_file(key, 'test.txt', 'test.enc')
    decrypt_file(key, 'test.enc', 'test.decrypt')


if __name__ == '__main__':
    ret = main()
    sys.exit(ret)


Comments, bug fixes, and suggestions are welcome. I am especially interested in hearing suggestions about applying it to common cases of backing up and hiding sensitive data of various types.

To run this, you will have to make sure you have all the imported modules in your Python installation. To test it, create a file called test.txt in the same directory as the program. The program will create (or overwrite) two new files, called test.enc, the encrypted file, and test.decrypt which should be identical to the test.txt.



0 blog comments below




FRIHOST HOME | FAQ | TOS | ABOUT US | CONTACT US | SITE MAP
© 2005-2011 Frihost, forums powered by phpBB.