Generating own key with Python Fernet
Asked Answered
A

5

20
from cryptography.fernet import Fernet

import base64
# Put this somewhere safe!
key = Fernet.generate_key()

f = Fernet()
token = f.encrypt(b"A really secret message. Not for prying eyes.")
token
print f.decrypt(token)

How can I generate my own key instead of fernet.genrate_key()?

Agneta answered 8/6, 2017 at 10:16 Comment(0)
A
8

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.

Annadiana answered 10/8, 2019 at 0:48 Comment(0)
O
24

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.

Oswald answered 13/6, 2017 at 16:37 Comment(1)
Is it cryptographically safe? I mean its randomness (don't know how to phrase it).Kirbie
A
9

In Bash, you can do:

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

Source: The Ruby implementation of Fernet

Acadian answered 29/5, 2018 at 2:14 Comment(0)
A
8

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.

Annadiana answered 10/8, 2019 at 0:48 Comment(0)
U
5

The python Cyrptography Fernet documentation has a note on generating a secure key using a password and salt:

import base64
import os
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

password = b"password"
salt = os.urandom(16)
kdf = PBKDF2HMAC(
    algorithm=hashes.SHA256(),
    length=32,
    salt=salt,
    iterations=390000,
)
key = base64.urlsafe_b64encode(kdf.derive(password))
f = Fernet(key)
token = f.encrypt(b"Secret message!")
token
# b'...'
f.decrypt(token)
# b'Secret message!'

Note, the salt must be stored along with the passcode in order to re-generate the same key; which makes this feel a bit redundant, although it does add a layer of obfuscation. Its likely best just to use Fernet.generate_key() and store that.

Unbecoming answered 26/6, 2022 at 21:2 Comment(0)
U
2

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
Unbecoming answered 26/6, 2022 at 4:49 Comment(2)
encode('latin-1')? why thisApothecary
@JAD - Because Base64 strings should always be in plain 7-bit ASCII, which should be 'latin-1'. bytes.encode() default of 'utf-8' would work fine but could be misleading to n00b's.Unbecoming

© 2022 - 2024 — McMap. All rights reserved.