Encrypting Data with Python
Encryption is a hot topic these days. I wanted to learn more about how it actually works. So I decided to make something to encrypt data.
One strategy to do this would be to use symmetric encryption. This uses the same key to encode and decode the data. But since the key has to be very accessible to do these tasks, it’s important to keep it safe.
This is where asymmetric encryption comes in. You may have heard of using a public/private key if you’ve messed around with SSH keys or even Bitcoin. Basically, you create a public key and a private key using prime numbers and really fancy math. With the way the math works out, if you encrypt data with the public key, you can’t un-encrypt it with that same public key. Only if you have the private key can you reverse the mathematical formula to reverse the encryption.
So what we’ll be doing is first create a public and private key (PU-KEY and PR-KEY from here on out), then another key (S-KEY) for symmetrical encryption of our data. We’ll use the PU-KEY to encrypt the S-KEY for safe keeping. Whenever we need to use the S-KEY, we’ll use the PR-KEY to un-encrypt it so we can encode and decode our data as needed.
Don’t get it? Well… let’s get started and see if you can catch on to what we’re doing.
Firstly, we’re going to be using Python for this, and you need a package called Cryptography. You should already have it, but if not, just do this:
pip install cryptography
Now we need a script to generate our key files.
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import serialization from cryptography.fernet import Fernet from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding private_key = rsa.generate_private_key( # Generate the private key public_exponent=65537, key_size=2048, backend=default_backend() ) public_key = private_key.public_key() # Generate the public key pem = private_key.private_bytes( # Format the private key as PEM encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() ) with open('private_key.pem', 'wb') as f: # Write the private key file f.write(pem) pem = public_key.public_bytes( # Format the public key as PEM encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ) with open('public_key.pem', 'wb') as f: # Write the public key file f.write(pem) key = Fernet.generate_key() # Generate the S-KEY encrypted = public_key.encrypt( # We'll encrypt the S-KEY while we're already here key, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) file = open('key.key', 'wb') # Save the encrypted S-KEY file.write(encrypted) file.close()
This will save three files: public_key.pem, private_key.pem, and key.key.
Now with this out of the way, we can encrypt some data. For this example, we’ll just do a text file (don.txt). This is from Don Quixote.
In a village of La Mancha, the name of which I have no desire to call to mind, there lived not long since one of those gentlemen that keep a lance in the lance-rack, an old buckler, a lean hack, and a greyhound for coursing. An olla of rather more beef than mutton, a salad on most nights, scraps on Saturdays, lentils on Fridays, and a pigeon or so extra on Sundays, made away with three-quarters of his income. The rest of it went in a doublet of fine cloth and velvet breeches and shoes to match for holidays, while on week-days he made a brave figure in his best homespun. He had in his house a housekeeper past forty, a niece under twenty, and a lad for the field and market-place, who used to saddle the hack as well as handle the bill-hook. The age of this gentleman of ours was bordering on fifty; he was of a hardy habit, spare, gaunt-featured, a very early riser and a great sportsman. They will have it his surname was Quixada or Quesada (for here there is some difference of opinion among the authors who write on the subject), although from reasonable conjectures it seems plain that he was called Quexana. This, however, is of but little importance to our tale; it will be enough not to stray a hair’s breadth from the truth in the telling of it.
You must know, then, that the above-named gentleman whenever he was at leisure (which was mostly all the year round) gave himself up to reading books of chivalry with such ardour and avidity that he almost entirely neglected the pursuit of his field-sports, and even the management of his property; and to such a pitch did his eagerness and infatuation go that he sold many an acre of tillageland to buy books of chivalry to read, and brought home as many of them as he could get. But of all there were none he liked so well as those of the famous Feliciano de Silva’s composition, for their lucidity of style and complicated conceits were as pearls in his sight, particularly when in his reading he came upon courtships and cartels, where he often found passages like “the reason of the unreason with which my reason is afflicted so weakens my reason that with reason I murmur at your beauty;” or again, “the high heavens, that of your divinity divinely fortify you with the stars, render you deserving of the desert your greatness deserves.” Over conceits of this sort the poor gentleman lost his wits, and used to lie awake striving to understand them and worm the meaning out of them; what Aristotle himself could not have made out or extracted had he come to life again for that special purpose. He was not at all easy about the wounds which Don Belianis gave and took, because it seemed to him that, great as were the surgeons who had cured him, he must have had his face and body covered all over with seams and scars. He commended, however, the author’s way of ending his book with the promise of that interminable adventure, and many a time was he tempted to take up his pen and finish it properly as is there proposed, which no doubt he would have done, and made a successful piece of work of it too, had not greater and more absorbing thoughts prevented him.
Now we need to make a script we can use to encrypt files.
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.fernet import Fernet import sys import os if len(sys.argv) > 3: filename = sys.argv # Path of file to encrypt skey = sys.argv # Path of our symmetrical key pkeyfile = sys.argv # path of our private key with open(pkeyfile, "rb") as key_file: # Load our private key private_key = serialization.load_pem_private_key( key_file.read(), password=None, backend=default_backend() ) f = open(skey, "rb") # Load our symmetrical key skeyfile = f.read() f.close() f = open(filename, "rb") # Load the file to encrypt text = f.read() f.close() unenc_skey = private_key.decrypt( # Decrypt our S-KEY skeyfile, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) f = Fernet(unenc_skey) # Encrypt our text file encrypted = f.encrypt(text) with open("encrypted_"+filename, 'wb') as f: # Save the encrypted file f.write(encrypted) else: print("Usage: "+os.path.basename(__file__)+ " filename.txt key.key private_key.pem")
This makes a file called encrypted_don.txt, which now looks like this:
Neat! Now let’s make a script to decrypt this.
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.fernet import Fernet import sys import os if len(sys.argv) > 3: filename = sys.argv # Path of file to decrypt skey = sys.argv # Path of our symmetrical key pkeyfile = sys.argv # path of our private key with open(pkeyfile, "rb") as key_file: # Load our private key private_key = serialization.load_pem_private_key( key_file.read(), password=None, backend=default_backend() ) f = open(skey, "rb") # Load our symmetrical key skeyfile = f.read() f.close() f = open(filename, "rb") # Load the file to decrypt text = f.read() f.close() unenc_skey = private_key.decrypt( # Decrypt our S-KEY skeyfile, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) f = Fernet(unenc_skey) decrypted = f.decrypt(text) with open("un"+filename, 'wb') as f: f.write(decrypted) else: print("Usage: "+os.path.basename(__file__)+ " filename.txt key.key private_key.pem")
Now we have a file called unencrypted_don.txt which has our original text.