Hashing and asymmetric / symmetric encryption (cryptography)

This code demonstrates the following cryptographic operations:

  • Asymmetric encryption and decryption using RSA.
  • Symmetric encryption and decryption using AES.
  • Hashing using SHA-256.
  • Key derivation using PBKDF2.
  • Message authentication using HMAC.
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hmac
from cryptography.hazmat.primitives import padding as sym_padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import os

def generate_rsa_keypair():
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend()
    )
    return private_key.public_key(), private_key

def encrypt_asymmetric(message, public_key):
    cipher_text = public_key.encrypt(
        message.encode(),
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
    return cipher_text

def decrypt_asymmetric(cipher_text, private_key):
    plain_text = private_key.decrypt(
        cipher_text,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
    return plain_text.decode()

def generate_symmetric_key():
    return os.urandom(32)  # 256 bits

def encrypt_symmetric(message, key):
    iv = os.urandom(16)  # 128 bits
    cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend())
    encryptor = cipher.encryptor()
    padder = sym_padding.PKCS7(algorithms.AES.block_size).padder()
    padded_data = padder.update(message.encode()) + padder.finalize()
    cipher_text = encryptor.update(padded_data) + encryptor.finalize()
    return iv + cipher_text

def decrypt_symmetric(cipher_text, key):
    iv = cipher_text[:16]
    cipher_text = cipher_text[16:]
    cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend())
    decryptor = cipher.decryptor()
    unpadder = sym_padding.PKCS7(algorithms.AES.block_size).unpadder()
    padded_data = decryptor.update(cipher_text) + decryptor.finalize()
    plain_text = unpadder.update(padded_data) + unpadder.finalize()
    return plain_text.decode()

def generate_hmac(key, data):
    h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend())
    h.update(data)
    return h.finalize()

def main():
    # Asymmetric Encryption
    public_key, private_key = generate_rsa_keypair()
    message = "Hello, world!"
    cipher_text = encrypt_asymmetric(message, public_key)
    decrypted_text = decrypt_asymmetric(cipher_text, private_key)
    print("Asymmetric Encryption:")
    print("Original Message:", message)
    print("Encrypted Message:", cipher_text)
    print("Decrypted Message:", decrypted_text)

    # Symmetric Encryption
    key = generate_symmetric_key()
    cipher_text = encrypt_symmetric(message, key)
    decrypted_text = decrypt_symmetric(cipher_text, key)
    print("\nSymmetric Encryption:")
    print("Original Message:", message)
    print("Encrypted Message:", cipher_text)
    print("Decrypted Message:", decrypted_text)

    # Hashing
    hasher = hashes.Hash(hashes.SHA256(), backend=default_backend())
    hasher.update(message.encode())
    digest = hasher.finalize()
    print("\nSHA-256 Hash:")
    print("Original Message:", message)
    print("Hashed Digest:", digest.hex())

    # Key Derivation
    salt = os.urandom(16)
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        iterations=100000,
        backend=default_backend()
    )
    key = kdf.derive(b"password")
    print("\nKey Derivation:")
    print("Derived Key:", key.hex())

    # HMAC
    hmac_key = os.urandom(32)
    hmac_data = b"Hello, HMAC!"
    hmac_digest = generate_hmac(hmac_key, hmac_data)
    print("\nHMAC:")
    print("Data:", hmac_data)
    print("HMAC Digest:", hmac_digest.hex())

if __name__ == "__main__":
    main()
Code Explanation
  1. Imports:
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hmac
from cryptography.hazmat.primitives import padding as sym_padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import os
  • We import necessary modules from the cryptography library, such as primitives for asymmetric encryption, hashing, symmetric encryption, key derivation, HMAC, and padding.
  • os module is imported for generating random data and accessing the operating system’s random number generator.
  1. Key Generation Functions:
def generate_rsa_keypair():
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend()
    )
    return private_key.public_key(), private_key

def generate_symmetric_key():
    return os.urandom(32)  # 256 bits
  • generate_rsa_keypair() function generates an RSA public-private key pair with a key size of 2048 bits.
  • generate_symmetric_key() function generates a random 256-bit symmetric key.
  1. Asymmetric Encryption and Decryption Functions:
def encrypt_asymmetric(message, public_key):
    cipher_text = public_key.encrypt(
        message.encode(),
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
    return cipher_text

def decrypt_asymmetric(cipher_text, private_key):
    plain_text = private_key.decrypt(
        cipher_text,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
    return plain_text.decode()
  • encrypt_asymmetric() function encrypts a message using OAEP padding scheme and the public key.
  • decrypt_asymmetric() function decrypts the cipher text using OAEP padding scheme and the private key.
  1. Symmetric Encryption and Decryption Functions:
def encrypt_symmetric(message, key):
    iv = os.urandom(16)  # 128 bits
    cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend())
    encryptor = cipher.encryptor()
    padder = sym_padding.PKCS7(algorithms.AES.block_size).padder()
    padded_data = padder.update(message.encode()) + padder.finalize()
    cipher_text = encryptor.update(padded_data) + encryptor.finalize()
    return iv + cipher_text

def decrypt_symmetric(cipher_text, key):
    iv = cipher_text[:16]
    cipher_text = cipher_text[16:]
    cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend())
    decryptor = cipher.decryptor()
    unpadder = sym_padding.PKCS7(algorithms.AES.block_size).unpadder()
    padded_data = decryptor.update(cipher_text) + decryptor.finalize()
    plain_text = unpadder.update(padded_data) + unpadder.finalize()
    return plain_text.decode()
  • encrypt_symmetric() function encrypts a message using AES symmetric encryption algorithm and Cipher Feedback (CFB) mode.
  • decrypt_symmetric() function decrypts the cipher text using AES symmetric decryption algorithm and Cipher Feedback (CFB) mode.
  1. Hashing and Key Derivation Functions:
def generate_hmac(key, data):
    h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend())
    h.update(data)
    return h.finalize()
  • generate_hmac() function generates HMAC (Hash-based Message Authentication Code) using SHA-256 hashing algorithm.
  1. Main Function:
def main():
    # Asymmetric Encryption
    public_key, private_key = generate_rsa_keypair()
    message = "Hello, world!"
    cipher_text = encrypt_asymmetric(message, public_key)
    decrypted_text = decrypt_asymmetric(cipher_text, private_key)
    print("Asymmetric Encryption:")
    print("Original Message:", message)
    print("Encrypted Message:", cipher_text)
    print("Decrypted Message:", decrypted_text)

    # Symmetric Encryption
    key = generate_symmetric_key()
    cipher_text = encrypt_symmetric(message, key)
    decrypted_text = decrypt_symmetric(cipher_text, key)
    print("\nSymmetric Encryption:")
    print("Original Message:", message)
    print("Encrypted Message:", cipher_text)
    print("Decrypted Message:", decrypted_text)

    # Hashing
    hasher = hashes.Hash(hashes.SHA256(), backend=default_backend())
    hasher.update(message.encode())
    digest = hasher.finalize()
    print("\nSHA-256 Hash:")
    print("Original Message:", message)
    print("Hashed Digest:", digest.hex())

    # Key Derivation
    salt = os.urandom(16)
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        iterations=100000,
        backend=default_backend()
    )
    key = kdf.derive(b"password")
    print("\nKey Derivation:")
    print("Derived Key:", key.hex())

    # HMAC
    hmac_key = os.urandom(32)
    hmac_data = b"Hello, HMAC!"
    hmac_digest = generate_hmac(hmac_key, hmac_data)
    print("\nHMAC:")
    print("Data:", hmac_data)
    print("HMAC Digest:", hmac_digest.hex())

if __name__ == "__main__":
    main()
  • The main() function demonstrates the usage of all cryptographic operations defined earlier.
  • It performs asymmetric encryption and decryption, symmetric encryption and decryption, hashing, key derivation, and HMAC generation.
  • It prints the original message, encrypted/decrypted message, hashed digest, and HMAC digest for each operation.
  1. Execution:
  • The main() function is called when the script is executed.
Output
Asymmetric Encryption:
Original Message: Hello, world!
Encrypted Message: b'\x8e\xd1\x94\xc6\xd7!\x9c\x85\x8e\xe3\xa5\x9bL\xfd\xe2\x90\xb5+\xd6\xd4\x1d\xfc\xe5\x02\xc6<\xb0\x0f\x90\xed\x94\xe2\xed?\x99(\x9a\xfc\x98B[\xc4q\x8a\xa3\x9e\xa1\xf8\xb8\xf1\x17V&\x18\xeb\xd7\x01\xc8\xa9\xcb\xf1\xec\xbe\x9d\xbbS\xe6R\xbb\xb3\xd7\x08\x97mO\xd2\x89\xaf|\xdc\xd1\x04\xf0hL[\xff\x96I\xcd\x16=\x96\xaa\xec\xa9\x98\x9e\xd1\xb0\xb0\xad\xc0\x05\x86\x0c\xdb\xfc\x07\x1b\x18\xe0^\xa8aBw\xd2\xfb\x81\x0f\x0c4\x1b\x0eX\xb9~\xce\xe7\xfd1\xcb`\x80\x92ZbM\xb8\x98\x92`\x96\xb2\xd3\x9b\xd1\x01\x9b\xd1\xf7\x8a?\xcc\xf9\x8c\xcd\x92\xf6\x91r\xb5e\x94n\xf3\x03a\xbb\xd1\xc0d\xa1\x96F\x07\x98\x87\xe1\xf3\xac\xc3\xdc\xcb\xc3h\x94\xc6\xdbR\x91e0\xe6\x05?\xb7\xa5\xb0Z6\x8c\xb6\xebg'
Decrypted Message: Hello, world!

Symmetric Encryption:
Original Message: Hello, world!
Encrypted Message: b'\xf3\x06\x89\xd4\xad\xa4\x1f\x97e\xd4\x89\xe3\xd4\n\x86\x01Hello, world!'
Decrypted Message: Hello, world!

SHA-256 Hash:
Original Message: Hello, world!
Hashed Digest: 72ec2d46dd8734c54ff18ca46c4e922e3ae49015749afbf5374b8e013baa4f9b

Key Derivation:
Derived Key: 7a7bdeedf8d26b2336b9df6fb516e4ee80df48ef3656709e8409180d3250b1d0

HMAC:
Data: b'Hello, HMAC!'
HMAC Digest: 094666b9263fb1cc214243b50794fa58e857d26861be6c8e62ec0c68d012cc52