This article is part of a series on the Python cryptography library.
Refer to the glossary of cryptography terms for definitions of any terms used in this chapter.
Fernet is a system for symmetric encryption/decryption, using current best practices. It also authenticates the message, which measm that the recipient can tell if the message has been altered in any way from what was originally sent.
Fernet overcomes many of the problems obvious mistakes a naive developer might make when designing such a system by:
We will look at the hows and whys of these features later in this article.
Fernet is included in the
To encrypt and decrypt data, we will need a secret key that must be shared between anyone who needs to encrypt or decrypt data. It must be kept secret from anyone else, because anyone who knows the key can read and create encrypted messages. This means we will need a secure mechanism to share the key. The same key can used multiple times.
We can create a key like this:
from cryptography.fernet import Fernet import base64 key = Fernet.generate_key()
The key is a random value, and will be completely different each time you call the
To encrypt a message, we must first create a
Fernet object using the key created previously. We than call the
encrypt function, passing the data we wish to encrypt is the form of a
cipher = Fernet(key) message = "Message to be encrypted".encode('utf-8') token = cipher.encrypt(message)
Notice that we use the
encode('uft-8') method to convert our message string into a bytes array. If we want to encrypt an image or other data, we must load it into memory as a byte array.
The encrypted message is stored in
token in the format described below.
To decrypt a message, we must again create a
Fernet object using the same key that was used to encrypt the data. We than call the
decrypt function, passing the data we wish to decrypt is the form of a
bytes array. The function returns the decrypted original message.
cipher = Fernet(key) decoded = cipher.decrypt(token)
decrypt will raise an exception if it cannot decode
token for any reason. For example:
Fernet tokens contain a timestamp which allows testing for an expired message. To do this you must add the a
ttl (time to live) parameter to the
decrypt function that specifies a maximum age (in seconds) of the token before it will be rejected. For example:
decoded = cipher.decrypt(token, 24*60*60)
This will reject any messages that are more than 1 day old. If you do not set a
ttl value (or set it to
None), the age of the token will not be checked at all.
A fernet key as returned by the
generate_key actually contains two 16-byte keys:
These two values are concatenated to form a 32 byte value:
This 32 byte key is then encoded using Base64 encoding. This encodes the binary quantity as string of ASCII characters. The variant of Base64 used is URL and filename safe, meaning that it doesn't contain any characters that aren't permitted in a URL or a valid filename in any major operating system.
If we print the key, the result is a 44 byte string representing the 32 byte bunary value.
print(key) # b'K7GVACyA63l--mNkBjQ5tbkDxO6yCJkmr9D-uV5T-wU='
A Fernet token contains the following fields:
The fields are:
The entire token (including the HMAC) is encoded using Base64. The HMAC is calculated using the binary data of the Version, Timestamp, IV, and Ciphertext fields, before Base64 encoding is applied.
Copyright (c) Axlesoft Ltd 2020