6

Advertisements

Hello everyone,

Michael here, and you may be wondering what’s up with this blog title? What could I possibly be covering? The joys of the number six perhaps?

First of all, this post marks the 6th anniversary of Michael’s Programming Bytes (known as Michael’s Analytics Blog until June 2022). Yes, dear readers, I have officially been blogging for six years now, with 166 posts to this blog’s name covering things from data analytics and Python coding to web development and GitHub.

Now, how might I style my anniversary post for year #6? Will I use it as an excuse to show you all something cool. Yes!

In fact, today I’ll be showing you how to work with basic text encryption in Python. Let’s begin!

What is encryption?

Now, before we dive into some Python encryption, let’s explain the concept of encryption as it relates to data.

Let’s use the example of sending an Instagram DM (direct message for those unfamiliar) to one of your friends. Instagram has the option to enable end-to-end encryption for any DMs you send, which means when you send a DM to your friend encryption would encode the text into something called ciphertext while the message is being sent to your friend’s device. Once the message reaches your friend’s device, it will be decrypted (or decoded) back to plain text.

Why is encryption important? When there’s a transfer of data from one point to another (like one person’s Instagram account to another’s), encryption essentially keeps any hackers from intercepting the contents of that data before it reaches its destination. After all, what good is ciphertext to a hacker?

There are two types of encryption I’ll show you-asymmetric-key encryption and symmetric-key encryption.

Symmetric-key encryption

The first type of encryption we’ll explore is symmetric-key encryption. In symmetric-key encryption, the text is encoded and decoded (or encrypted and decrypted) with the same key.

Although this is an easier method of encryption than asymmetric-key encryption (which we’ll discuss later in this post), it is also a less secure method because the same key is being used to encrypt and decrypt the message. As long as anyone has the encryption/decryption key, they can read the message.

Here’s an illustration of the idea of symmetric-key encryption.

In this picture, “the key” represents the key needed to both encrypt and decrypt the message.

Now, let’s explore symmetric-key encryption in a Pythonic sense. First, please pip install cryptography before beginning.

Next, let’s see some symmetric-key encryption in action:

from cryptography.fernet import Fernet

message = "Thank you for six wonderful years!"

theKey = Fernet.generate_key()

theFernetKey = Fernet(theKey)

encryptedMessage = theFernetKey.encrypt(message.encode())

print("The original message is: " , message)
print("The encrypted message is: " , encryptedMessage)

decryptedMessage = theFernetKey.decrypt(encryptedMessage).decode()

print("The decrypted message is: " , decryptedMessage)

print("The encryption key is: " , theKey)

And here’s our output:

The original message is:  Thank you for six wonderful years!
The encrypted message is:  b'gAAAAABmaQb-Ft-ws6utm0lw8S7Vl-ZHeW0MKyYLdYbGrrV-t04xzjg4ftpQ_0oOegR2MzQ8KWeOsfV2-UMzZdR10CM_UeWmpnlkSOW6kBnYn4KYE9bV-f0mBrco9zWS-OvePTvkGU4F'
The decrypted message is:  Thank you for six wonderful years!
The encryption key is:  b'TA5gu709UF8GEw6zIVvq77sWaOzQHPShZaMlUmA17ls='

So, how did I accomplish all this. Let’s walk through the code, shall we?

  • I imported the Fernet class from the cryptography package. Fernet encryption is a type of symmetric-key encryption that ensures a message can’t be read or manipulated without the encryption/decryption key.
  • I also had a message that I wanted to encrypt and decrypt using Fernet encryption.
  • Before encrypting my message, I utilized the Fernet.generate_key() method to generate an encryption/decryption key (storing it in the theKey variable) and then ensured the key utilized Fernet encryption by instantiating it as an object of the Fernet class (using the line Fernet(theKey) and storing it in the theFernetKey variable).
  • I then encrypted my message by using the Fernet key’s encrypt() method and passing in message.encode() as the method’s parameter. This parameter will encode the original message using Fernet encryption.
  • After printing out my original message and encrypted message, I then decrypted the message using the Fernet key’s decrypt() method while passing in the encryptedMessage as the parameter. I then followed up the call to the decrypt() method with a call to the decode() method.
  • Finally, I printed out my decrypted message and (non-Fernet) encryption key. Granted, I didn’t build this script with high security in mind, but assuming someone had the encryption key, they could read and mess around with my message.

Now, the fun thing about encryption in Python is that, if we try to encrypt the same message using symmetric-key encryption, we’ll get a different key created each time. Here’s the key that’s generated when I run this script again:

b'OkncdE57Dvq42ODTxSMdLbpEIJeZWr5b2_Gbej1LevU='

And now for some asymmetric-key encryption

Now that we’ve explored symmetric-key encryption, let’s explore asymmetric-key encryption! Unlike symmetric-key encryption, asymmetric-key encryption uses different keys to encrypt and decrypt data. A public key encrypts the data while a private key decrypts the data-the great thing about this setup is that since no one has the private key, no one can access the data you are trying to transmit. If it helps, think of asymmetric-key encryption like two-factor authentication (you know when you have to use both your password and a second code to login to something), where the different keys add two layers of protection for your data.

Just as I did with symmetric-key encryption, here’s an illustration of asymmetric-key encryption:

And now, let’s code! For this example, please pip install rsa to be able to follow along with this lesson.

Here’s our code for the asymmetric-key encryption:

import rsa

publicKey, privateKey = rsa.newkeys(512)

message = 'Thank you loyal readers for six amazing years'

encryptedMessage = rsa.encrypt(message.encode(), publicKey)

print('Original message: ', message)
print('Encrypted message: ', encryptedMessage)

decryptedMessage = rsa.decrypt(encryptedMessage, privateKey).decode()

print('Decrypted message: ', decryptedMessage)

print('Public key: ', publicKey)
print('Private key: ', privateKey)

And here are the outputs:

Original message:  Thank you loyal readers for six amazing years
Encrypted message:  b'X\xaa\xc5\x98\xe2\xc8\xd1"\xd5\x94\xd0\xc2l\x92\xe3\xc4^\xe9\xef\x83\x18\xab\xdc\xfb\xea\xbb\x1a9\x06\x8e"\xa1\x08\xcc:\xa6n\xc3\xa4\xc2\x14F\xe5i\x96\xd4\x0e\xb6B\x9c-\x85"\xd9\xde\x15\xd8S\xba\xb8\xc8s\x88m'
Decrypted message:  Thank you loyal readers for six amazing years
Public key:  PublicKey(8756745001992373161285778726645083782004419876731866636961799474661459252554364385770004594397922925180145618274212925790191421654715585611349812414582633, 65537)
Private key:  PrivateKey(8756745001992373161285778726645083782004419876731866636961799474661459252554364385770004594397922925180145618274212925790191421654715585611349812414582633, 65537, 2577171637371696805390544273914435753655206228274169456852147462765616764969150310852001220742439294959750117227094790559647443735723327989042277493248225, 7128026561941571600154499219580762398618969604207116659373371614354183291477318639, 1228495001512334734540841979883773428960324113167485626876315020518609447)

So, how did I accomplish all of this? Let’s walk through the code, shall we?

  • The RSA module that we used here utilizes the RSA encryption algorithm, which is a type of encryption algorithm that uses two different but linked keys (one public and one private). The public key encrypts the message while the private key decrypts the message.
  • Quick historical fact: the name RSA comes from the surnames of the creators of this algorithm-computer scientists Ron Rivest, Adi Shamir and Leonard Adleman, who first developed this algorithm in 1977 (all of them are still alive as of June 2024)
  • I used the rsa.newkeys() method to create the publicKey and privateKey and passed in 512 as this method’s parameter-the 512 represents the number of bits each key should have. Trying to figure out a good number of bits to utilize is a little trial-and-error process.
  • I then used the rsa.encrypt() method to encrypt my message and passed in both message.encode() and my publicKey as parameters.
  • After printing out the original and encrypted message, I then used the rsa.decrypt() method to decrypt my message and passed in the encryptedMessage and privateKey as this method’s parameters.
  • I finally printed out the decryptedMessage, publicKey and privateKey.

One interesting thing to note is the similarities between the publicKey and privateKey. Remember how I mentioned that these two keys are opposite, albeit linked? Notice how both keys start with 8756745001992373161285778726645083782004419876731866636961799474661459252554364385770004594397922925180145618274212925790191421654715585611349812414582633, 65537. However, the privateKey is considerably longer than the publicKey, likely to make it harder to access.

Also, similar to symmetric-key encryption, this script will generate different keys each time its run. Here’s the keys we get after another run of the script:

PublicKey(7855075279758572094336135232248306022642803736898164846214110092559099915389472776042423258530713061811745645535500011244055637229684973871741305772152203, 65537)

PrivateKey(7855075279758572094336135232248306022642803736898164846214110092559099915389472776042423258530713061811745645535500011244055637229684973871741305772152203, 65537, 999608279798991276176257195736009768967773672364171305025034380150798682275873600573017565727711649304440962386673322170031172276957264982184527359310433, 6478929829895505402335617053533719083904398041145802512468037497716457622395698959, 1212403203305762871531606015835489318412721597481677658992522607891186117)

You’ll also notice that whenever I run this script, the RSA keys I obtain always have the number 65537 in them. Why might that be? It’s what’s known as a public exponent in the RSA algorithm, which is a crucial part of the public key that is utilized for verifying both encryption of the data and access signatures for anyone trying to access the data.

Dear coder, thank you

However you decide to encode this message, I just want to make one thing clear-thank you, thank you, thank you for following along this journey with me for six wonderful years. Thank you for reading everything I’ve done over the past six years (and perhaps learning a trick of the trade along the way)? I hope to keep coding along for as long as possible but I’ll admit, I’ve certainly come a long way since my early posts (remember R Lesson 1: Basic R commands as my first-ever tutorial and second overall post?). I’ve also certainly learned quite a bit about running this publication over the last six years, and to be honest, I feel like my programming has come very very far since that first post in summer 2018.

In short, keep calm and code along fellow devs! I’ll be back with another great year of programming content (and perhaps another cool coding demo for the 7th anniversary).

Also, I would be remiss not to acknowledge the two furry friends that have been around since the early days of this blog:

Orange Boy/Simba and Pretty Girl/Marbles (seen here eagerly awaiting their Christmas presents in 2017):

Michael