Encrypting and Decrypting Messages

Cryptography is a crucial aspect of modern digital security, enabling secure communication and data protection. This article explores practical encryption and decryption techniques using Python, providing code examples and real-world use cases.

⚠️
The code examples in this article are for educational purposes only. For production use, always rely on well-established cryptographic libraries and follow best practices for key management and secure implementation.

Basic Encryption and Decryption

Let’s start with a simple example using the cryptography library in Python.

basic_encryption.py
from cryptography.fernet import Fernet

# Generate a key
key = Fernet.generate_key()

# Create a Fernet instance
f = Fernet(key)

# Encrypt a message
message = b"Hello, World!"
encrypted_message = f.encrypt(message)

print("Encrypted:", encrypted_message)

# Decrypt the message
decrypted_message = f.decrypt(encrypted_message)

print("Decrypted:", decrypted_message.decode())

This example demonstrates basic encryption and decryption using the Fernet algorithm, which is a high-level symmetric encryption method.

Asymmetric Encryption with RSA

For more advanced scenarios, we can use asymmetric encryption like RSA:

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

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

# Encrypt message
message = b"Secret message"
encrypted = public_key.encrypt(
    message,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

print("Encrypted:", encrypted)

# Decrypt message
decrypted = private_key.decrypt(
    encrypted,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

print("Decrypted:", decrypted.decode())

This example showcases RSA encryption, which is commonly used for secure key exchange and digital signatures.

File Encryption

Encrypting files is another common use case:

file_encryption.py
from cryptography.fernet import Fernet

def encrypt_file(file_path, key):
    f = Fernet(key)
    with open(file_path, 'rb') as file:
        file_data = file.read()
    encrypted_data = f.encrypt(file_data)
    with open(file_path + '.encrypted', 'wb') as file:
        file.write(encrypted_data)

def decrypt_file(file_path, key):
    f = Fernet(key)
    with open(file_path, 'rb') as file:
        encrypted_data = file.read()
    decrypted_data = f.decrypt(encrypted_data)
    with open(file_path[:-10], 'wb') as file:
        file.write(decrypted_data)

# Usage
key = Fernet.generate_key()
encrypt_file('secret_document.txt', key)
decrypt_file('secret_document.txt.encrypted', key)

This example provides functions to encrypt and decrypt files, which can be useful for securing sensitive documents.

Password Hashing

While not strictly encryption, password hashing is a crucial security practice:

password_hashing.py
import bcrypt

def hash_password(password):
    salt = bcrypt.gensalt()
    hashed = bcrypt.hashpw(password.encode(), salt)
    return hashed

def check_password(password, hashed):
    return bcrypt.checkpw(password.encode(), hashed)

# Usage
password = "mysecretpassword"
hashed_password = hash_password(password)
print("Hashed password:", hashed_password)

# Checking password
is_correct = check_password(password, hashed_password)
print("Password correct:", is_correct)

This example demonstrates how to securely hash and verify passwords using bcrypt, which is essential for storing user credentials safely.

Elliptic Curve Cryptography (ECC)

Elliptic Curve Cryptography offers strong security with smaller key sizes compared to RSA. Here’s an example using the ecdsa library:

ecc_example.py
from ecdsa import SigningKey, NIST384p
import os

# Generate a new key pair
private_key = SigningKey.generate(curve=NIST384p)
public_key = private_key.get_verifying_key()

# Sign a message
message = b"Hello, Elliptic Curve Cryptography!"
signature = private_key.sign(message)

# Verify the signature
assert public_key.verify(signature, message)
print("Signature verified successfully!")

This example demonstrates key generation, signing, and verification using the NIST P-384 curve.

Ed25519 Encryption

Ed25519 is a modern, fast, and secure signature scheme. Here’s how to use it with the nacl library:

ed25519_example.py
import nacl.utils
from nacl.public import PrivateKey, Box

# Generate key pair
alice_private_key = PrivateKey.generate()
alice_public_key = alice_private_key.public_key

bob_private_key = PrivateKey.generate()
bob_public_key = bob_private_key.public_key

# Create shared box
box = Box(alice_private_key, bob_public_key)

# Encrypt a message
message = b"Secret message using Ed25519"
encrypted = box.encrypt(message)

# Decrypt the message
decrypted = Box(bob_private_key, alice_public_key).decrypt(encrypted)

print("Original:", message)
print("Decrypted:", decrypted)

This example showcases key generation, encryption, and decryption using Ed25519-based public-key cryptography.

Symmetric vs Asymmetric Encryption

To illustrate the difference between symmetric and asymmetric encryption, let’s use a Mermaid diagram:

graph TD
    A[Plaintext] --> B{Encryption Type}
    B -->|Symmetric| C[Shared Secret Key]
    B -->|Asymmetric| D[Public Key]
    C --> E[Encrypted Data]
    D --> E
    E --> F{Decryption}
    F -->|Symmetric| G[Shared Secret Key]
    F -->|Asymmetric| H[Private Key]
    G --> I[Plaintext]
    H --> I

This diagram shows how symmetric encryption uses a single shared key for both encryption and decryption, while asymmetric encryption uses a public key for encryption and a private key for decryption.

Mathematical Foundations

Cryptography relies heavily on mathematical concepts. For instance, the security of RSA is based on the difficulty of factoring large numbers. The RSA encryption function can be represented as:

$$ C = M^e \mod n $$

Where:

  • $C$ is the ciphertext
  • $M$ is the plaintext message
  • $e$ is the public exponent
  • $n$ is the modulus (product of two large prime numbers)

The decryption function is:

$$ M = C^d \mod n $$

Where $d$ is the private exponent.

Conclusion

Encryption and decryption are fundamental to digital security. By understanding and implementing these techniques, you can protect sensitive data, secure communications, and enhance the overall security of your applications. Remember to always use well-tested libraries and follow best practices when implementing cryptographic solutions in real-world scenarios.