Cryptography cơ bản với Python

Tìm hiểu về cryptography trong Python - hashing, symmetric/asymmetric encryption, digital signatures.

Cryptography là gì?

Cryptography là nghệ thuật bảo vệ thông tin bằng cách chuyển đổi thành dạng không đọc được (mã hóa) và chỉ có thể đọc lại bằng key đúng (giải mã).

Cài đặt

pip install cryptography

Hashing

Hashing là quá trình một chiều - không thể “giải mã” hash value.

import hashlib

# MD5 (không nên dùng cho security)
text = "Hello, PION!"
md5_hash = hashlib.md5(text.encode()).hexdigest()
print(f"MD5: {md5_hash}")

# SHA-256 (khuyến nghị)
sha256_hash = hashlib.sha256(text.encode()).hexdigest()
print(f"SHA256: {sha256_hash}")

# SHA-512
sha512_hash = hashlib.sha512(text.encode()).hexdigest()
print(f"SHA512: {sha512_hash}")

# Hash file
def hash_file(filepath: str, algorithm: str = "sha256") -> str:
    """Calculate hash of a file."""
    hash_func = hashlib.new(algorithm)
    
    with open(filepath, "rb") as f:
        for chunk in iter(lambda: f.read(8192), b""):
            hash_func.update(chunk)
    
    return hash_func.hexdigest()

# Usage
file_hash = hash_file("document.pdf")
print(f"File hash: {file_hash}")

Password Hashing

Sử dụng bcrypt hoặc argon2 cho passwords:

import bcrypt

# Hash password
password = "my_secure_password"
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password.encode(), salt)
print(f"Hashed: {hashed}")

# Verify password
def check_password(password: str, hashed: bytes) -> bool:
    return bcrypt.checkpw(password.encode(), hashed)

# Usage
if check_password("my_secure_password", hashed):
    print("Password correct!")
else:
    print("Wrong password!")

Symmetric Encryption (Fernet)

Cùng key dùng để encrypt và decrypt:

from cryptography.fernet import Fernet

# Generate key
key = Fernet.generate_key()
print(f"Key: {key.decode()}")

# Save key to file
with open("secret.key", "wb") as f:
    f.write(key)

# Load key
with open("secret.key", "rb") as f:
    key = f.read()

# Create cipher
cipher = Fernet(key)

# Encrypt
message = "This is a secret message"
encrypted = cipher.encrypt(message.encode())
print(f"Encrypted: {encrypted}")

# Decrypt
decrypted = cipher.decrypt(encrypted)
print(f"Decrypted: {decrypted.decode()}")

File Encryption

from cryptography.fernet import Fernet
from pathlib import Path

def encrypt_file(filepath: str, key: bytes) -> str:
    """Encrypt a file."""
    cipher = Fernet(key)
    
    with open(filepath, "rb") as f:
        data = f.read()
    
    encrypted = cipher.encrypt(data)
    
    encrypted_path = filepath + ".encrypted"
    with open(encrypted_path, "wb") as f:
        f.write(encrypted)
    
    return encrypted_path

def decrypt_file(filepath: str, key: bytes) -> str:
    """Decrypt a file."""
    cipher = Fernet(key)
    
    with open(filepath, "rb") as f:
        encrypted = f.read()
    
    decrypted = cipher.decrypt(encrypted)
    
    # Remove .encrypted extension
    original_path = filepath.replace(".encrypted", "")
    with open(original_path, "wb") as f:
        f.write(decrypted)
    
    return original_path

# Usage
key = Fernet.generate_key()
encrypt_file("secret.txt", key)
decrypt_file("secret.txt.encrypted", key)

AES Encryption

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os

def aes_encrypt(plaintext: bytes, key: bytes) -> tuple:
    """Encrypt using AES-256-CBC."""
    
    # Generate random IV
    iv = os.urandom(16)
    
    # Create cipher
    cipher = Cipher(
        algorithms.AES(key),
        modes.CBC(iv),
        backend=default_backend()
    )
    
    # Pad plaintext to block size
    padding_length = 16 - (len(plaintext) % 16)
    padded = plaintext + bytes([padding_length] * padding_length)
    
    # Encrypt
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(padded) + encryptor.finalize()
    
    return iv, ciphertext

def aes_decrypt(iv: bytes, ciphertext: bytes, key: bytes) -> bytes:
    """Decrypt AES-256-CBC."""
    
    cipher = Cipher(
        algorithms.AES(key),
        modes.CBC(iv),
        backend=default_backend()
    )
    
    decryptor = cipher.decryptor()
    padded = decryptor.update(ciphertext) + decryptor.finalize()
    
    # Remove padding
    padding_length = padded[-1]
    plaintext = padded[:-padding_length]
    
    return plaintext

# Usage
key = os.urandom(32)  # 256-bit key
message = b"Secret message"

iv, encrypted = aes_encrypt(message, key)
decrypted = aes_decrypt(iv, encrypted, key)
print(decrypted.decode())

Asymmetric Encryption (RSA)

Dùng public key để encrypt, private key để decrypt:

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

# Generate key pair
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048
)
public_key = private_key.public_key()

# Save private key
with open("private_key.pem", "wb") as f:
    f.write(private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    ))

# Save public key
with open("public_key.pem", "wb") as f:
    f.write(public_key.public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo
    ))

# Encrypt với public key
message = b"Secret message"
ciphertext = public_key.encrypt(
    message,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

# Decrypt với private key
plaintext = private_key.decrypt(
    ciphertext,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)
print(plaintext.decode())

Digital Signatures

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

# Generate key pair
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048
)
public_key = private_key.public_key()

# Sign message
message = b"This is an important document"
signature = private_key.sign(
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)

# 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 is valid!")
except:
    print("Invalid signature!")

Ứng dụng: Simple Encryption Tool

#!/usr/bin/env python3
"""
Simple file encryption tool
"""

import argparse
from cryptography.fernet import Fernet
from pathlib import Path

def generate_key() -> bytes:
    return Fernet.generate_key()

def encrypt_file(filepath: str, key: bytes):
    cipher = Fernet(key)
    
    with open(filepath, "rb") as f:
        data = f.read()
    
    encrypted = cipher.encrypt(data)
    
    with open(filepath + ".enc", "wb") as f:
        f.write(encrypted)
    
    print(f"Encrypted: {filepath}.enc")

def decrypt_file(filepath: str, key: bytes):
    cipher = Fernet(key)
    
    with open(filepath, "rb") as f:
        data = f.read()
    
    decrypted = cipher.decrypt(data)
    
    output = filepath.replace(".enc", "")
    with open(output, "wb") as f:
        f.write(decrypted)
    
    print(f"Decrypted: {output}")

def main():
    parser = argparse.ArgumentParser(description="File Encryption Tool")
    parser.add_argument("action", choices=["generate", "encrypt", "decrypt"])
    parser.add_argument("-f", "--file", help="File to encrypt/decrypt")
    parser.add_argument("-k", "--key", help="Key file")
    
    args = parser.parse_args()
    
    if args.action == "generate":
        key = generate_key()
        with open("encryption.key", "wb") as f:
            f.write(key)
        print("Key saved to encryption.key")
    
    elif args.action in ["encrypt", "decrypt"]:
        with open(args.key, "rb") as f:
            key = f.read()
        
        if args.action == "encrypt":
            encrypt_file(args.file, key)
        else:
            decrypt_file(args.file, key)

if __name__ == "__main__":
    main()

Bước tiếp theo

Tiếp theo:

  • Password Tools: Generators và crackers
  • Packet Sniffing: Phân tích network traffic

⚠️ Security tip: Không bao giờ hardcode keys trong code. Sử dụng environment variables hoặc key management systems!