Python’s Cryptography Library

Python’s cryptography library is a powerful toolkit designed to provide secure cryptographic operations. It’s widely used for encryption, decryption, signing, verification, and more. Whether you need symmetric encryption, asymmetric cryptography, or hashing, the cryptography library offers a comprehensive set of tools to meet your needs.

Why Use cryptography?

  • Secure: Implements modern cryptographic standards and best practices.
  • Versatile: Offers symmetric and asymmetric encryption, digital signatures, key derivation, and more.
  • Well-Maintained: Actively maintained and reviewed by security experts.
  • User-Friendly: Designed to be easy to use without compromising security.

Key Features of cryptography

Symmetric Encryption

Supports common symmetric encryption algorithms like AES, including modes like GCM, CBC, and more.

Asymmetric Encryption

Provides support for RSA, DSA, and EC keys, offering encryption and digital signatures.

Hashing

Supports secure hashing algorithms like SHA-2 and SHA-3, and HMACs.

Key Derivation

Derive secure keys from passwords using PBKDF2, scrypt, and other algorithms.

X.509 Certificates

Create, parse, and manage X.509 certificates and CRLs.

Secure Random Numbers

Generate cryptographically secure random numbers and strings.

Installing the Library

To use cryptography, install it via pip:

pip install cryptography

Symmetric Encryption

Symmetric encryption uses a single key for both encryption and decryption. The cryptography library provides support for AES with modes like CBC and GCM.

AES Encryption (CBC)

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
from cryptography.hazmat.backends import default_backend
import os

key = os.urandom(32)  # 32 bytes for AES-256
iv = os.urandom(16)  # Initialization vector

cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
decryptor = cipher.decryptor()

# Padding the plaintext to match the block size
from cryptography.hazmat.primitives import padding

padder = padding.PKCS7(128).padder()
plaintext = b"Secret Message!"
padded_data = padder.update(plaintext) + padder.finalize()

# Encrypting the message
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
print(f"Ciphertext: {ciphertext}")

# Decrypting the message
unpadder = padding.PKCS7(128).unpadder()
decrypted_data = decryptor.update(ciphertext) + decryptor.finalize()
decrypted_message = unpadder.update(decrypted_data) + unpadder.finalize()

print(f"Decrypted Message: {decrypted_message.decode()}")

AES Encryption (GCM)

GCM provides both encryption and integrity checking.

from cryptography.hazmat.primitives.ciphers.aead import AESGCM

key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)
nonce = os.urandom(12)  # Recommended size for GCM nonce

plaintext = b"Secret message"
associated_data = b"Additional data"

# Encrypt
ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data)
print(f"Ciphertext: {ciphertext}")

# Decrypt
decrypted_text = aesgcm.decrypt(nonce, ciphertext, associated_data)
print(f"Decrypted Message: {decrypted_text.decode()}")

Asymmetric Encryption

Asymmetric encryption involves a pair of keys (public and private). The cryptography library supports RSA, DSA, and EC.

RSA Encryption

from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes

# Generate RSA keys
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())
public_key = private_key.public_key()

# Encrypting
message = b"Secure Message"
ciphertext = public_key.encrypt(
    message,
    padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)
)
print(f"Ciphertext: {ciphertext}")

# Decrypting
plaintext = private_key.decrypt(
    ciphertext,
    padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)
)
print(f"Decrypted Message: {plaintext.decode()}")

Digital Signatures

Digital signatures provide authenticity verification. The cryptography library supports RSA, DSA, and EC.

RSA Signatures

from cryptography.hazmat.primitives import serialization

# Sign data
signature = private_key.sign(
    message,
    padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256()
)
print(f"Signature: {signature}")

# Verify signature
try:
    public_key.verify(
        signature,
        message,
        padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
        hashes.SHA256()
    )
    print("Signature Verified")
except Exception as e:
    print(f"Signature Verification Failed: {e}")

Hashing and HMACs

Hashing ensures data integrity, and HMACs protect data integrity and authenticity.

SHA-256 Hashing

from cryptography.hazmat.primitives import hashes

digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(b"Message to hash")
hash_result = digest.finalize()
print(f"SHA-256 Hash: {hash_result.hex()}")

HMAC with SHA-256

from cryptography.hazmat.primitives import hmac

key = os.urandom(32)
h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend())
h.update(b"Message to hash")
hmac_result = h.finalize()
print(f"HMAC: {hmac_result.hex()}")

Key Derivation

Key derivation securely derives cryptographic keys from passwords.

PBKDF2

kdf = PBKDF2HMAC(
    algorithm=hashes.SHA256(),
    length=32,
    salt=os.urandom(16),
    iterations=100000,
    backend=default_backend()
)
key = kdf.derive(b"my password")
print(f"Derived Key: {key.hex()}")

Scrypt

kdf = Scrypt(
    salt=os.urandom(16),
    length=32,
    n=2**14,
    r=8,
    p=1,
    backend=default_backend()
)
key = kdf.derive(b"my password")
print(f"Derived Key: {key.hex()}")

X.509 Certificates

Generate and manage X.509 certificates.

Self-Signed Certificate

from cryptography import x509
from cryptography.x509.oid import NameOID
import datetime

subject = issuer = x509.Name([
    x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
    x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"California"),
    x509.NameAttribute(NameOID.LOCALITY_NAME, u"San Francisco"),
    x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"My Company"),
    x509.NameAttribute(NameOID.COMMON_NAME, u"mycompany.com"),
])
cert = (
    x509.CertificateBuilder()
    .subject_name(subject)
    .issuer_name(issuer)
    .public_key(public_key)
    .serial_number(x509.random_serial_number())
    .not_valid_before(datetime.datetime.utcnow())
    .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=10))
    .sign(private_key, hashes.SHA256(), default_backend())
)
print(cert)

Secure Random Numbers

Generating cryptographically secure random numbers:

from cryptography.hazmat.primitives import constant_time

secure_random_number = os.urandom(32)
print(f"Secure Random Number: {secure_random_number.hex()}")

Conclusion

Python’s cryptography library is a comprehensive toolkit for secure cryptographic operations. It supports encryption, decryption, digital signatures, hashing, key derivation, and secure random number generation.