We will be using Python 3.8.10 for this Python AES 256 Encryption Example.
AES (Advanced Encryption Standard) was originally called Rijndael and is a symmetric block algorithm for encrypting or decrypting data. The standard was established by the U.S. National Institute of Standards and Technology (NIST) in 2001.
For this tutorial we will be using the pycryptodome library/module, which we can install with the following command at the terminal (if you don’t have it already):
pip install pycryptodomex
3 simple steps to use AES 256 bit encryption in Python:
- Generate a 256bit encryption key
- Use the key to create a cipher
- Encrypt the data with the cipher
Now let’s write our code.
First, let’s encrypt our data:
import hashlib
import os
from Cryptodome.Cipher import AES
from Cryptodome.Random import get_random_bytes
data=b"DATA_TO_BE_ENCRYPTED"
password=b"PASSWORD"
salt = get_random_bytes(32)
key = hashlib.scrypt(password, salt=salt, n=2**14, r=8, p=1, dklen=32)
cipher = AES.new(key, AES.MODE_GCM)
ciphertext, tag = cipher.encrypt_and_digest(data)
file_out = open("encryptedfile.bin", "wb")
[ file_out.write(x) for x in (cipher.nonce, tag, ciphertext) ]
file_out.close()
Let’s explain what is going on here:
- We import all the modules we will use.
- We have our data and password. Byte literals are always prefixed with ‘b’ or ‘B’.
- We generate a salt using get_random_bytes which returns a random byte string of length N. N in this case is 32 bytes (256 bits). The salt is used to provide additional security for our key.
- We generate our key using scrypt. This method takes the password, salt, CPU/Memory Cost Factor (n), the block size (r), the parallelization factor (p) and the length of the derived key (dklen). Because we are using AES 256, we must use 32 bytes as the dklen.
- We generate our cipher using AES.new(). It takes 2 arguments: the key in bytes, which we defined with the previous statement, and the mode which is a constant. In this case we are using Galois/Counter Mode (GCM) which is a high performance mode.
- We call encrypt_and_digest() which performs encryption and digest. Recall that encryption conceals the contents of our data, while a digest is a fixed size numeric representation which acts as an identifier for the contents of the data. The encrypt_and_digest method accepts our data and returns a tuple with the ciphertext and the message authentication code (MAC), sometimes known as a tag, which confirms the authenticity and authority of the data.
- Finally, we write our encrypted message as ciphertext to the encryptedfile.bin file on disk (same directory as python script) along with the tag and a cipher.nonce. The cipher.nonce is an arbitrary value used only once to ensure that our data is original. If, for example, we see a cipher.nonce used more than once for different pieces of data, we know that security was compromised.
Now, let’s learn how to decrypt our data. Whoever is going to decrypt and consume our data will need access to the password, the salt, ciphertext, nonce and tag.
file_in = open("encryptedfile.bin", "rb")
nonce, tag, ciphertext = [ file_in.read(x) for x in (16, 16, -1) ]
key = hashlib.scrypt(password, salt=salt, n=2**14, r=8, p=1, dklen=32)
cipher = AES.new(key, AES.MODE_GCM, nonce)
data = cipher.decrypt_and_verify(ciphertext, tag)
print(data.decode('UTF-8'))Â
Let’s explain what is happening here:
- We access the file with the encrypted data to read in the bytes. Once we do that, we will retrieve the nonce, tag and ciphertext.
- We provide our password, the retrieved salt and the other parameters to re-generate our key.
- We use AES.new() to create our cipher, as we did before but this time we include our nonce. Normally only the person decrypting the message should have access to the key.
- We call decrypt_and_verify, pass the ciphertext and tag and our decrypted data is returned to us. Yaaaay!
- Our data is in bytes so we must use method decode before we can use print() to show us the message in the console.
That’s it! Below is our full working code:
import hashlib
import os
from Cryptodome.Cipher import AES
from Cryptodome.Random import get_random_bytes
data=b"DATA_TO_BE_ENCRYPTED"
password=b"PASSWORD"
salt = get_random_bytes(32)
key = hashlib.scrypt(password, salt=salt, n=2**14, r=8, p=1, dklen=32)
cipher = AES.new(key, AES.MODE_GCM)
ciphertext, tag = cipher.encrypt_and_digest(data)
file_out = open("encryptedfile.bin", "wb")
[ file_out.write(x) for x in (cipher.nonce, tag, ciphertext) ]
file_out.close()
#########################################################################
file_in = open("encryptedfile.bin", "rb")
nonce, tag, ciphertext = [ file_in.read(x) for x in (16, 16, -1) ]
key = hashlib.scrypt(password, salt=salt, n=2**14, r=8, p=1, dklen=32)
cipher = AES.new(key, AES.MODE_GCM, nonce)
data = cipher.decrypt_and_verify(ciphertext, tag)
print(data.decode('UTF-8'))Â
We hope that helped! AES encryption has many applications including file and storage encryption, communications encryption and passwords just to name a few. Find out more about it HERE.
Thanks for reading. 👌👌👌