How to encrypt and decrypt data in Python 3 using pycrypto

When you wish to encrypt and decrypt data in your Python 3 application, you can take a look at pycrypto.

Given that, let us look at how we can encrypt and decrypt data in Python 3 using pycrpto.

Installing pycrypto into your Python 3 environment

In order to use pycrypto, we need to install it.

Therefore, run the following command to install pycrypto into your Python 3 environment:

pip pycrypto

Getting an instance of the AES to encrypt and decrypt data with the AES encryption algorithm

After you had installed pycrypto in your Python 3 environment, you can then choose an encryption algorithm to encrypt and decrypt your data.

For example, you can write the following Python 3 codes to get an object to encrypt / decrypt data with the AES encryption algorithm:

from Crypto.Cipher import AES

# AES key must be either 16, 24, or 32 bytes long
COMMON_ENCRYPTION_KEY='asdjk@15r32r1234asdsaeqwe314SEFT'
# Make sure the initialization vector is 16 bytes
COMMON_16_BYTE_IV_FOR_AES='IVIVIVIVIVIVIVIV'

def get_common_cipher():
    return AES.new(COMMON_ENCRYPTION_KEY,
                   AES.MODE_CBC,
                   COMMON_16_BYTE_IV_FOR_AES)

As shown above, we first import the AES module. After we had done so, we define an encryption key that is 32 bytes long. In case you are wondering, this key must be either 16, 24 or 32 bytes long.

After that, we define an initialization vector that must be 16 bytes long.

Once we have defined the key and initialization vector, we then define a function to get an AES cipher instance.

Whenever we need to perform encryption or decryption, we can use the get_common_cipher function.

Since the cipher object is stateful, we should create a new AES cipher instance whenever we wish to encrypt or decrypt data.

How to encrypt string in Python 3 using pycrypto

When we represent our data as string or text, we can transfer our data easily with HTTP.

Given that, let's look at how we can define a function to encrypt string:

import base64
import math
def encrypt_with_common_cipher(cleartext):
    common_cipher = get_common_cipher()
    cleartext_length = len(cleartext)
    next_multiple_of_16 = 16 * math.ceil(cleartext_length/16)
    padded_cleartext = cleartext.rjust(next_multiple_of_16)
    raw_ciphertext = common_cipher.encrypt(padded_cleartext)
    return base64.b64encode(raw_ciphertext).decode('utf-8')

As shown above, we first import the base64 and math modules.

Once we have done so, we define a function encrypt_with_common_cipher that takes a string as an input.

When the function is called, we first get an instance of the AES cipher to perform the encryption.

Since the cipher does not pad our data, we need to do that on our own. Therefore, we first get the length of the text data to compute the next multiple of 16. Once we get the next multiple of 16, we use the rjust method to pad the cleartext with spaces.

Once we had padded our string data to make its size a multiple of 16, we then encrypt it with the AES cipher. When we do so, raw_ciphertext will contain the corresponding cipher text in bytes.

In order to convert the raw_ciphertext to a string, we call base64.b64encode on raw_ciphertext, followed by decode before returning the result to the caller.

How to decrypt string in Python 3 using pycrypto

Whenever we encrypt our string data, there will be a point in time when we want to decrypt it.

Given that, we can define a function to decrypt the cipher text that was created by encrypt_with_common_cipher:

def decrypt_with_common_cipher(ciphertext):
    common_cipher = get_common_cipher()
    raw_ciphertext = base64.b64decode(ciphertext)
    decrypted_message_with_padding = common_cipher.decrypt(raw_ciphertext)
    return decrypted_message_with_padding.decode('utf-8').strip()

Similar to encrypt_with_common_cipher, we first get an instance of the AES cipher with the same key and initialization vector.

Next, we take the ciphertext, convert it back to bytes and kept it as raw_ciphertext.

Once we get back the cipher text in bytes, we use our AES cipher to decrypt it.

When we do so, we will get the decrypted message with padding.

Finally, we decode decrypted_message_with_padding as a string, call strip to remove the spaces and return the result to the caller.

How to encrypt JSON data in Python 3 using pycrypto

At this point in time, encrypting JSON data will be straightforward:

import json
def encrypt_json_with_common_cipher(json_obj):
    json_string = json.dumps(json_obj)
    return encrypt_with_common_cipher(json_string)

As shown above, we can define a encrypt_json_with_common_cipher function that takes a JSON object as input.

When the function is called, we use json.dumps to convert the JSON object into a JSON string.

Once we have the JSON string, we pass it to the encrypt_with_common_cipher function and return the result back to the caller.

How to decrypt JSON data in Python 3 using pycrypto

When we want to get back the JSON data that we had encrypted, we can define the following function:

import json
def decrypt_json_with_common_cipher(json_ciphertext):
    json_string = decrypt_with_common_cipher(json_ciphertext)
    return json.loads(json_string)

As shown above, the decrypt_json_with_common_cipher function takes in a JSON cipher text as an input.

When the function is called, we call the decrypt_with_common_cipher function to get back the JSON string.

Once we have the JSON string, we use json.loads to get back the JSON object and return it back to the caller.

Putting everything together

In case you want a running example of what was discussed, you can run the following script:

from Crypto.Cipher import AES

import base64, json, math

# AES key must be either 16, 24, or 32 bytes long
COMMON_ENCRYPTION_KEY='asdjk@15r32r1234asdsaeqwe314SEFT'
# Make sure the initialization vector is 16 bytes
COMMON_16_BYTE_IV_FOR_AES='IVIVIVIVIVIVIVIV'

def get_common_cipher():
    return AES.new(COMMON_ENCRYPTION_KEY,
                   AES.MODE_CBC,
                   COMMON_16_BYTE_IV_FOR_AES)

def encrypt_with_common_cipher(cleartext):
    common_cipher = get_common_cipher()
    cleartext_length = len(cleartext)
    nearest_multiple_of_16 = 16 * math.ceil(cleartext_length/16)
    padded_cleartext = cleartext.rjust(nearest_multiple_of_16)
    raw_ciphertext = common_cipher.encrypt(padded_cleartext)
    return base64.b64encode(raw_ciphertext).decode('utf-8')


def decrypt_with_common_cipher(ciphertext):
    common_cipher = get_common_cipher()
    raw_ciphertext = base64.b64decode(ciphertext)
    decrypted_message_with_padding = common_cipher.decrypt(raw_ciphertext)
    return decrypted_message_with_padding.decode('utf-8').strip()


def encrypt_json_with_common_cipher(json_obj):
    json_string = json.dumps(json_obj)
    return encrypt_with_common_cipher(json_string)


def decrypt_json_with_common_cipher(json_ciphertext):
    json_string = decrypt_with_common_cipher(json_ciphertext)
    return json.loads(json_string)

message = 'This is obviously a secret message.'

ciphertext = encrypt_with_common_cipher(message)
print('Cipher text: %s' % ciphertext)

decrypted_message = decrypt_with_common_cipher(ciphertext)
print('Decrypted message: %s' % decrypted_message)

json_obj = {
    'fruits': ['apple', 'pear', 'orange']
}

json_ciphertext = encrypt_json_with_common_cipher(json_obj)
print('JSON cipher text: ')
print(json_ciphertext)

decryped_json_obj = decrypt_json_with_common_cipher(json_ciphertext)
print('Decrypted JSON object: ')
print(decryped_json_obj)

After the function definition for decrypt_json_with_common_cipher, we proceeded to encrypt and decrypt a string and a JSON object.

When you run the script, you should get the following output:

Cipher text: Qq3sD/JX0M2Uo5anotgnBhZvyM/KHGWc/Eaoin1ocnoTrPNSZpKUUKtTuzW+ocJs
Decrypted message: This is obviously a secret message.
JSON cipher text: 
Tj/KLrTiFQfUJtQOe9FuUkHoMI4BKxjQAtrPyAYxGpDrlOvtcLIUKhVmRhloqNdm
Decrypted JSON object: 
{'fruits': ['apple', 'pear', 'orange']}

About Clivant

Clivant a.k.a Chai Heng enjoys composing software and building systems to serve people. He owns techcoil.com and hopes that whatever he had written and built so far had benefited people. All views expressed belongs to him and are not representative of the company that he works/worked for.