Python cryptography 

Installation

pip install cryptography

Introduction

cryptography includes both high level recipes and low level interfaces to common cryptographic algorithms such as symmetric ciphers, message digests, and key derivation functions. For example, to encrypt something with cryptography’s high level symmetric encryption recipe:

from cryptography.fernet import Fernet
# Put this somewhere safe!
key = Fernet.generate_key()
print(key.hex())
f = Fernet(key)
token = f.encrypt(b"A really secret message. Not for prying eyes.")
print(token)
# b'...'
print(f.decrypt(token))
# b'A really secret message. Not for prying eyes.'
print(token.hex())

Layout

cryptography is broadly divided into two levels. One with safe cryptographic recipes that require little to no configuration choices. These are safe and easy to use and don’t require developers to make many decisions.

The other level is low-level cryptographic primitives. These are often dangerous and can be used incorrectly. They require making decisions and having an in-depth knowledge of the cryptographic concepts at work. Because of the potential danger in working at this level, this is referred to as the “hazardous materials” or “hazmat” layer. These live in the cryptography.hazmat package, and their documentation will always contain an admonition at the top.

We recommend using the recipes layer whenever possible, and falling back to the hazmat layer only when necessary.

Example

สร้าง key แล้วเก็บไว้ในไฟล์ secret.key

from cryptography.fernet import Fernet

# Use Fernet to generate the key file.
key = Fernet.generate_key()

# Store the file to disk to be accessed for en/de:crypting later.
with open('secret.key', 'wb') as new_key_file:
    new_key_file.write(key)
print(key)
# print(key.decode('utf-8'))
print(key.hex())

นำ key จากในไฟล์ secret.key มา encrypt ข้อความ

from cryptography.fernet import Fernet
# Load the private key from a file.
with open('secret.key', 'rb') as my_private_key:
    key = my_private_key.read()

msg = "Into the valley of death, rode the 600."
# Encode this as bytes to feed into the algorithm.
# (Refer to Encoding types above).
msg = msg.encode()
# print(msg)

# Instantiate the object with your key.
f = Fernet(key)
# Pass your bytes type message into encrypt.
ciphertext = f.encrypt(msg)
print(ciphertext)

นำ key จากในไฟล์ secret.key มา decrypt ข้อความ

from cryptography.fernet import Fernet
# Load the private key from a file.
with open('secret.key', 'rb') as my_private_key:
    key = my_private_key.read()

# Instantiate Fernet on the recip system.
f = Fernet(key)

ciphertext = b'gAAAAABi9LSeFJbUUaMH3Ra-xDJkj_U_1xbs2dpzhoHQT2mTCSLIYQYBMvqYIJtrkzu1jI-IyTQCiLGCvKZfcB09Fq_wfUuAVZneIn3mg3Nz3QV4vUvT3N5xk0Iu1VfpGmdWIkrUET8_'

# Decrypt the message.
cleartext = f.decrypt(ciphertext)
# Decode the bytes back into a string.
cleartext = cleartext.decode()
print(cleartext)

Using passwords with Fernet

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)  # IV
print(salt.hex())
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!")
print(token)
# b'...'
print(f.decrypt(token))
# b'Secret message!'

In this scheme, the salt has to be stored in a retrievable location in order to derive the same key from the password in the future.

The iteration count used should be adjusted to be as high as your server can tolerate. A good default is at least 480,000 iterations, which is what Django recommends as of July 2022.

Implementation

Fernet is built on top of a number of standard cryptographic primitives. Specifically it uses:

  • AES in CBC mode with a 128-bit key for encryption; using PKCS7 padding.
  • HMAC using SHA256 for authentication.
  • Initialization vectors are generated using os.urandom().

The CBC mode requires an IV, and this IV is generated by os.urandom(). (Python 3 Fernet encrypting same message different ways – Stack Overflow)