Generating own key with Python Fernet

Here is how to do this using a passcode, unsalted. Note this method does not generate a very secure key:

from cryptography.fernet import Fernet
import base64, hashlib

def gen_fernet_key(passcode:bytes) -> bytes:
    assert isinstance(passcode, bytes)
    hlib = hashlib.md5()
    hlib.update(passcode)
    return base64.urlsafe_b64encode(hlib.hexdigest().encode('latin-1'))

Usage:

passcode = '249524.405925.606329'
key = gen_fernet_key(passcode.encode('utf-8'))
fernet = Fernet(key)
data_in = "SOME DATA"
cypher_text = fernet.encrypt(data_in.encode('utf-8'))
decr_data = fernet.decrypt(cypher_text).decode('utf-8')

print(f"passcode: {passcode}")
print(f"data_in: {data_in}")
print(f"key: {key}")
print(f"cypher_text: {cypher_text}")
print(f"decr_data: {decr_data}")

Output:

passcode: 249524.405925.606329
data_in: SOME DATA
key: b'NWRmMTk3ZWUwY2RjNjA3NWY4NzQ2NmQyOGRkYzczMmM='
cypher_text: b'gAAAAABit-SGVb4JMb-AWCetN-T029YzxQBkRou3fQElSY0zidJbM7M5w5TeJzIacyMaFycmUxFPYrSDgDnOrhC0OggtJ_xDMw=='
decr_data: SOME DATA

In fernet a key can be generated using one of fernet's Key Derivation Functions

One of the functions provided by fernet is the 'Password Based Key Derivation Function 2'. An example that uses PBKDF2HMAC can be found at Using Passwords with Fernet. This is discussed in git issue #1333 of pyca/cryptography, maebert points out that the example uses salt=os.urandom(16) and will generate a new key from a password each time the kdf class is constructed with a different salt value.

If you need to use a custom key derivation function look at source code for kdf and pbkdf2 to have an example of a class that implements the KeyDerivationFunction interface.

A class that matches its signature and implements the interface should be able to be dropped in as a custom key derivation function.


In Bash, you can do:

dd if=/dev/urandom bs=32 count=1 2>/dev/null | openssl base64

Source: The Ruby implementation of Fernet


The implementation shows how this is done:

return base64.urlsafe_b64encode(os.urandom(32))

So to generate your own you'll want to generate 32 cryptographically secure random bytes and then urlsafe base64 encode them. Of course, since generate_key already does this you should probably just call that unless you need to generate the key outside of your Python process.