Wednesday, July 10, 2013

Encryption in Python (PyCrypto)

I have been experimenting with the PyCrypto module for Python as a way of encrypting data. I am using version 2.6 on Python 2.7. I installed the Windows binary file found here. For testing purposes I created a text file "file2encrypt.txt" which contains the text: "For your eyes only! My secret message." This is what I hope to encrypt and decrypt. To create my RSA key I do the following:
from Crypto.PublicKey import RSA
from Crypto import Random

KEYSIZE = 256 * 8

def readfile(filename):
    fh = open(filename, 'rb')
    string = fh.read()
    fh.close()
    return string
    
def writefile(filename, string):
    fh = open(filename, 'wb')
    fh.write(string)
    fh.close()

random_generator = Random.new().read
RSAkey = RSA.generate(KEYSIZE, 
                      randfunc=random_generator, 
                      progress_func=None, 
                      e=65537)
public_key = RSAkey.publickey()

# Export the public key
pke = public_key.exportKey(format='PEM', passphrase=readfile('public_passphrase.txt'), pkcs=1)
writefile('../Public/public_key.txt', pke)

# Export the private key
pke = RSAkey.exportKey(format='PEM', passphrase=readfile('private_passphrase.txt'), pkcs=1)
writefile('private_key.txt', pke)
This generates a 2048-bit RSA key from which I can extract the public key. I export the key to two text files: 'public_key.txt' and 'private_key.txt'. As their names suggest the public key may be shared but the private key is meant for the sender and receiver only. The public key may be used only to encrypt data whereas the private key may be used to encrypt and decrypt data. There is an extra password/passphrase on the key export process which is needed by both parties to import the key(s). With my key in place I can now encrypt my file2encrypt.txt file.
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
import cPickle

def readfile(filename):
    fh = open(filename, 'rb')
    string = fh.read()
    fh.close()
    return string
    
def writefile(filename, string):
    fh = open(filename, 'wb')
    fh.write(string)
    fh.close()
    
def write_serial(filename, data):
    fh = open(filename, 'wb')
    cPickle.dump(data, fh, protocol=cPickle.HIGHEST_PROTOCOL)
    fh.close()

PASSPHRASE_PRIVATE = readfile('private_passphrase.txt')
plainfile = readfile('file2encrypt.txt')

RSAkey = readfile('private_key.txt')
RSAkey = RSA.importKey(RSAkey, passphrase=PASSPHRASE_PRIVATE)

h = SHA256.new(plainfile)
signer = PKCS1_v1_5.new(RSAkey)
signature = signer.sign(h)

# Save signature
write_serial('signature.pkl', signature)

# Encrypt file
write_serial('../Public/encryptedfile.pkl', RSAkey.encrypt(plainfile, ''))
This script loads my file2encrypt and the RSA key using the "importKey" function. The author of the encrypted file has the option to "sign" the file so that the recipient has a means of verifying that the encrypted file did indeed come from the sender (ie. the message wasn't changed during transmission). I create a serial signature.pkl file using the cPickle module. The data is encrypted via the RSAkey.encrypt() function and sent to the recipient.
On the recipient end we need to decrypt the file and verify the signature. To accomplish this I exectute the following code. This code imports the private key and verifies the signature of the message generated by the author.
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
import cPickle

def readfile(filename):
    fh = open(filename, 'rb')
    string = fh.read()
    fh.close()
    return string
    
def read_serial(filename):
    fh = open(filename, 'rb')
    data = cPickle.load(fh)
    fh.close()
    return data
     

encodedfile = read_serial('../Public/encryptedfile.pkl')

RSAkey = readfile('private_key.txt')
RSAkey = RSA.importKey(RSAkey, passphrase=readfile('private_passphrase.txt'))

# Decrypt data
plaindata = RSAkey.decrypt(encodedfile)

# Verify author
h = SHA256.new(plaindata)
verifier = PKCS1_v1_5.new(RSAkey)
signature = read_serial('signature.pkl')
if verifier.verify(h, signature):
    print "The signature is authentic.\n"
    print plaindata
else:
    print "The signature is not authentic."

If you print the string "plaindata" you will see "For your eyes only! My secret message". We have succeeded in decrypting the original message and verifying it's authenticity.


No comments: