How to Encrypt and Decrypt Data in Python using Cryptography Library
In this example, we will be using symmetric encryption, which means the same key we used to encrypt data, is also usable for decryption.
The cryptography library that we use here is built on top of AES algorithm.
Encrypt Data in Python
First, we need to install the cryptography library:
pip3 install cryptography
From the cryptography library, we need to import Fernet and start generating a key — this key is required for symmetric encryption/decryption.
Generate Key
To generate a key, we call the generate_key() method:
from cryptography.fernet import Fernet def generate_key(): """ Generates a key and save it into a file """ key = Fernet.generate_key() with open("secret.key", "wb") as key_file: key_file.write(key)
We only need to execute the above method once to generate a key.
Note: You need to keep this key in a safe place. If you lose the key, you won’t be able to decrypt the data that was encrypted with this key.
Load the Key
Once we have generated a key, we need to load the key in our method in order to encrypt data:
def load_key(): """ Loads the key named `secret.key` from the current directory. """ return open("secret.key", "rb").read()
Encrypt a Message
Now, we are ready to encrypt a message. This is a three step process:
- 1 — encode the message
- 2 — initialize the Fernet class
- 3 — pass the encoded message to encrypt() method
encode the message:
message = "message I want to encrypt".encode()
initialize the Fernet class:
encrypt the message:
encrypted_message = f.encrypt(message)
Full Code Example
Below is a full working example of encrypting a message in python:
from cryptography.fernet import Fernet def generate_key(): """ Generates a key and save it into a file """ key = Fernet.generate_key() with open("secret.key", "wb") as key_file: key_file.write(key) def load_key(): """ Load the previously generated key """ return open("secret.key", "rb").read() def encrypt_message(message): """ Encrypts a message """ key = load_key() encoded_message = message.encode() f = Fernet(key) encrypted_message = f.encrypt(encoded_message) print(encrypted_message) if __name__ == "__main__": encrypt_message("encrypt this message")
b'gAAAAABesCUIAcM8M-_Ik_-I1-JD0AzLZU8A8-AJITYCp9Mc33JaHMnYmRedtwC8LLcYk9zpTqYSaDaqFUgfz-tcHZ2TQjAgKKnIWJ2ae9GDoea6tw8XeJ4='
Decrypt Data in Python
To decrypt the message, we just call the decrypt() method from the Fernet library. Remember, we also need to load the key as well, because the key is needed to decrypt the message.
from cryptography.fernet import Fernet def load_key(): """ Load the previously generated key """ return open("secret.key", "rb").read() def decrypt_message(encrypted_message): """ Decrypts an encrypted message """ key = load_key() f = Fernet(key) decrypted_message = f.decrypt(encrypted_message) print(decrypted_message.decode()) if __name__ == "__main__": decrypt_message(b'gAAAAABesCUIAcM8M-_Ik_-I1-JD0AzLZU8A8-AJITYCp9Mc33JaHMnYmRedtwC8LLcYk9zpTqYSaDaqFUgfz-tcHZ2TQjAgKKnIWJ2ae9GDoea6tw8XeJ4=')
Encryption and Decryption in Python
In this post, I discuss how to encrypt and decrypt messages in Python using symmetric encryption. I will demonstrate how to create keys, save keys and how to encrypt messages and text.
Using the cryptography module in Python, we will use an implementation of AES called Fernet to encrypt data. I will also show you how to keep keys safe and how to use these methods on files.
Since Python does not come with anything that can encrypt files, we will need to use a third-party module.
PyCrypto is quite popular but since it does not offer built wheels, if you don’t have Microsoft Visual C++ Build Tools installed, you will be told to install it. Instead of installing extra tools just to build this, I will be using the cryptography module. To install this, execute:
python -m pip install cryptography
To make sure it installed correctly, open IDLE and execute:
If no errors appeared it has been installed correctly.
What is Symmetric Encryption?
Symmetric encryption is when a key is used to encrypt and decrypt a message, so whoever encrypted it can decrypt it. The only way to decrypt the message is to know what was used to encrypt it; kind of like a password.
To use symmetric encryption, we will use the Fernet class which is an implementation of AES
There are two main ways to get a key, we can either generate a new one or use one that has previously been generated. These keys need to be in a particular format so make sure to get this right.
To generate a new random key, we can simply use
from cryptography.fernet import Fernet key = Fernet.generate_key()
The variable key will now have the value of a URL safe base64 encoded key. When using these keys to encrypt, make sure to keep them safe, if you lose them you will not be able to decrypt your message.
This key will have a type of bytes, so if you want a string you can call key.decode() to convert from UTF-8 to Pythons string type.
One way of keeping your keys safe is to keep them in a file. To do this we can simply create/overwrite a file and put the key in it.
file = open('key.key', 'wb') # Open the file as wb to write bytes file.write(key) # The key is type bytes still file.close()
Make sure to keep these files safe and don’t give them to anyone that you don’t trust. Anyone with these keys can decrypt all past messages encrypted with this key.
If you have previously saved your key using the method I showed, you can read the key back out using the following code.
file = open('key.key', 'rb') # Open the file as wb to read bytes key = file.read() # The key will be type bytes file.close()
The key will now be read into the variable key and will be type bytes.
Generating a Key From A Password
If you want to base your key of a string that the user can input or some other form of input, you can create a key using this input.
import base64 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC password_provided = "password" # This is input in the form of a string password = password_provided.encode() # Convert to type bytes salt = b'salt_' # CHANGE THIS - recommend using a key from os.urandom(16), must be of type bytes kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000, backend=default_backend() ) key = base64.urlsafe_b64encode(kdf.derive(password)) # Can only use kdf once
The variable key will now have the value of a url safe base64 encoded key.
It is recommended to use a different salt than the one shown here. You can generate a new salt using os.urandom(16). Make sure to use the same salt every time you convert a password to a key otherwise it will not produce the same result.
To encrypt a message, you will need a key (as previously discussed) and your message as type bytes (you can convert strings to bytes using .encode() ).
from cryptography.fernet import Fernet message = "my deep dark secret".encode() f = Fernet(key) encrypted = f.encrypt(message) # Encrypt the bytes. The returning object is of type bytes
The variable encrypted will now have the value of the message encrypted as type bytes. This is also a URL safe base64 encoded key.
To decrypt a message, you will need the same key and the encrypted message (still in bytes).
from cryptography.fernet import Fernet encrypted = b". encrypted bytes. " f = Fernet(key) decrypted = f.decrypt(encrypted) # Decrypt the bytes. The returning object is of type bytes
The variable decrypted will now have the value of the original message (which was of type bytes).
If a different key to the one used to encrypt is provided when decrypting, a cryptography.fernet.InvalidToken will be raised.
Catching this error will allow you to tell if the incorrect key was provided; for example:
from cryptography.fernet import Fernet, InvalidToken encrypted = b". encrypted bytes. " f = Fernet(incorrect_key) # An example of providing the incorrect key try: decrypted = f.decrypt(encrypted) print("Valid Key - Successfully decrypted") except InvalidToken as e: # Catch any InvalidToken exceptions if the correct key was not provided print("Invalid Key - Unsuccessfully decrypted")
To show this in action, here is a properly constructed example.
>>> from cryptography.fernet import Fernet >>> message = "my deep dark secret".encode() # .encode() is used to turn the string to bytes >>> key = Fernet.generate_key() # Store this key or get if you already have it >>> f = Fernet(key) >>> encrypted = f.encrypt(message) >>> decrypted = f.decrypt(encrypted) >>> message == decrypted # The original message and decrypted bytes are the same True >>>
This example shows a key being generated, you will want to make sure you have already sorted your key out and put it in a file for later use.
Encrypting and Decrypting Files
We can also encrypt files using this method since files can be read as bytes. Simply open the file, read the bytes, encrypt the data and write them out to a new file. To encrypt:
from cryptography.fernet import Fernet key = b'' # Use one of the methods to get a key (it must be the same when decrypting) input_file = 'test.txt' output_file = 'test.encrypted' with open(input_file, 'rb') as f: data = f.read() # Read the bytes of the input file fernet = Fernet(key) encrypted = fernet.encrypt(data) with open(output_file, 'wb') as f: f.write(encrypted) # Write the encrypted bytes to the output file # Note: You can delete input_file here if you want
And then to decrypt a file:
from cryptography.fernet import Fernet, InvalidToken key = b'' # Use one of the methods to get a key (it must be the same as used in encrypting) input_file = 'test.encrypted' output_file = 'test.txt' with open(input_file, 'rb') as f: data = f.read() # Read the bytes of the encrypted file fernet = Fernet(key) try: decrypted = fernet.decrypt(data) with open(output_file, 'wb') as f: f.write(decrypted) # Write the decrypted bytes to the output file # Note: You can delete input_file here if you want except InvalidToken as e: print("Invalid Key - Unsuccessfully decrypted")
As stated in Fernet docs, beware of large files; Fernet is ideal for encrypting data that easily fits in memory. You may need to think of methods to split larger files up to use this encryption method on large files.
Owner of PyTutorials and creator of auto-py-to-exe. I enjoy making quick tutorials for people new to particular topics in Python and tools that help fix small things.