Practical 1
AIM : Write a program to store username and password in an encrypted form in a
database to implement integrity lock.
Python code:
pip install cryptography
import sqlite3
from cryptography.fernet import Fernet
# Generate a key for encryption and decryption
# You must use this key to decrypt your encrypted data
key = Fernet.generate_key()
cipher_suite = Fernet(key)
# Connect to the SQLite database (or create it if it doesn't exist)
conn = sqlite3.connect('user_data.db')
cursor = conn.cursor()
# Create a table to store username and encrypted password
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
password TEXT NOT NULL
)
''')
conn.commit()
def add_user(username, password):
"""Function to add a new user with an encrypted password."""
encrypted_password = cipher_suite.encrypt(password.encode())
cursor.execute('''
INSERT INTO users (username, password) VALUES (?, ?)
''', (username, encrypted_password))
conn.commit()
print(f"User {username} added successfully.")
# Example usage
add_user('alice', 'alice_password')
add_user('bob', 'bob_password')
# Function to retrieve and print users with encrypted passwords
def print_encrypted_users():
cursor.execute('SELECT * FROM users')
rows = cursor.fetchall()
for row in rows:
user_id, username, encrypted_password = row
print(f'ID: {user_id}, Username: {username}, Encrypted Password:
{encrypted_password}')
# Print the encrypted users
print_encrypted_users()
# Close the connection
conn.close()
Practical 4
Write a program to implement SSL.
Server
import socket
import ssl
def create_ssl_server(host, port):
# Create a socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((host, port))
sock.listen(5)
# Wrap the socket with SSL
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile="self_signed_certificate.pem", keyfile="private_key.pem")
print(f"Server listening on {host}:{port}")
while True:
client_socket, client_address = sock.accept()
print(f"Connection from {client_address}")
# Wrap the client socket with SSL
ssl_client_socket = context.wrap_socket(client_socket, server_side=True)
try:
data = ssl_client_socket.recv(1024)
print(f"Received: {data.decode()}")
ssl_client_socket.sendall(b"Hello, SSL client!")
finally:
ssl_client_socket.close()
create_ssl_server('localhost', 8080)
Client
import socket
import ssl
def create_ssl_client(host, port):
context = ssl._create_unverified_context() # Use an unverified context
# Connect to the server
with socket.create_connection((host, port)) as sock:
with context.wrap_socket(sock, server_hostname=host) as ssock:
print("Connection established!")
ssock.sendall(b"Hello, SSL server!")
data = ssock.recv(1024)
print(f"Received: {data.decode()}")
create_ssl_client('localhost', 8080) # Ensure the port matches the server's port
Generating Self-Signed Certificates
Creating a self-signed certificate using OpenSSL is straightforward. Here's a step-by-step
guide:
Step 1: Install OpenSSL
Make sure OpenSSL is installed on your system. You can check this by running:
openssl>version
Step 2: Generate a Private Key
You can generate a private key using the following command:
openssl>genpkey -algorithm RSA -out private_key.pem -pkeyopt
rsa_keygen_bits:2048
This creates a 2048-bit RSA private key and saves it to private_key.pem.
Step 3: Create a Certificate Signing Request (CSR)
Next, generate a CSR using the private key:
openssl>req -new -key private_key.pem -out certificate.csr -config
C:\xampp\php\extras\ssl\openssl.cnf
You'll be prompted to enter information such as country, state, organization, and common
name (the domain name or IP address).
Step 4: Generate a Self-Signed Certificate
Finally, create the self-signed certificate using the CSR and the private key:
openssl>x509 -req -in certificate.csr -signkey private_key.pem -out
self_signed_certificate.pem -days 365
This command generates a self-signed certificate valid for 365 days and saves it as
self_signed_certificate.pem.
Summary of Files Created
● private_key.pem: Your private key.
● certificate.csr: The certificate signing request.
● self_signed_certificate.pem: The self-signed certificate.
Step 5: Verify the Certificate
You can verify the contents of your self-signed certificate with:
bash
Copy code
openssl>x509 -in self_signed_certificate.pem -text -noout
Running the Program
1. Run the Server:
Execute the server script to start the SSL server:
python ssl_server.py
2. Run the Client:
○ Execute the client script to connect to the SSL server and exchange
messages securely:
python ssl_client.py
Practical 6 : Write a program to digitally sign MIME to create an ‘opaque’ signature.
MIME (Multipurpose Internet Mail Extensions) is a standard used to extend the format of
email messages to support text in character sets other than ASCII, as well as attachments of
audio, video, images, and application programs.
MIME is essential for modern email communication, enabling rich content and diverse media
types. It’s widely used in email clients, web applications, and APIs to facilitate sending and
receiving complex messages.
● The term "opaque" refers to a signature that is not directly interpretable or visible in
its content.
● An opaque signature hides the specific details of the signed content. This means that
the signature itself does not reveal any information about the content it is signing,
which enhances security.
● When a message is signed with an opaque signature, the recipient can verify that the
message hasn't been altered since it was signed. However, the signature doesn't
provide insights into what the original message contains beyond confirming its
integrity.
● The primary goal of an opaque signature is to ensure that the recipient can validate
the authenticity of the message without needing to understand the signature's inner
workings or exposing the original content.
mime.py
import base64
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
# Generate RSA keys
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
public_key = private_key.public_key()
def create_mime_message(subject, body):
"""Creates a simple MIME message."""
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = "
[email protected]"
msg['To'] = "
[email protected]"
# Attach the body
text_part = MIMEText(body, 'plain')
msg.attach(text_part)
return msg
def sign_message(message):
"""Signs the MIME message and returns the signature."""
message_bytes = message.as_bytes()
signature = private_key.sign(
message_bytes,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
return base64.b64encode(signature).decode('utf-8')
# Create MIME message
subject = "Test Email"
body = "This is a test email with a digital signature."
mime_message = create_mime_message(subject, body)
# Sign the message
signature = sign_message(mime_message)
# Output the MIME message and the signature
print("MIME Message:")
print(mime_message.as_string())
print("\nDigital Signature (Base64):")
print(signature)
Verifying the Signature
To verify the signature, you can use the public key and compare the signature against the
original message:
def verify_signature(message, signature):
"""Verifies the digital signature of the MIME message."""
message_bytes = message.as_bytes()
signature_bytes = base64.b64decode(signature)
try:
public_key.verify(
signature_bytes,
message_bytes,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
return True
except Exception as e:
print(f"Verification failed: {e}")
return False
# Verify the signature
is_valid = verify_signature(mime_message, signature)
print("\nSignature Valid:", is_valid)
Explanation:
The code demonstrates how to create a MIME message, digitally sign it, and attach the
signature as a separate part.
The use of cryptography ensures the integrity and authenticity of the message.
This approach is useful in scenarios where you need to ensure that the message has not
been altered and comes from a legitimate sender.
Step 1: Import Required Libraries
import base64
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa,
padding
base64: This module is used for encoding binary data into ASCII text, making it suitable for
transmission.
email.mime: These classes help create and manipulate MIME messages. MIMEText is for
plain text, and MIMEMultipart is for messages that may contain multiple parts.
cryptography: This library provides tools for encryption, hashing, and digital signatures. The
specific components used here include:
○ rsa: For generating RSA key pairs.
○ hashes: For defining the hash algorithm used in signing.
○ padding: To specify the padding scheme for the signature.
Step 2: Generate RSA Keys
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
public_key = private_key.public_key()
Here, we generate an RSA private key.
The public_exponent is typically set to 65537, which is a common choice for security and
performance.
The key_size of 2048 bits is a good balance of security for most applications.
The public_key method retrieves the corresponding public key for later verification.
The public exponent is part of the mathematical operations used to encrypt data and verify
signatures.
It is typically chosen to be a small integer that satisfies certain mathematical properties to
ensure security and efficiency.
The most commonly used public exponent is 65537.
It is chosen because it is a prime number and has a binary representation of
10000000000000001, which allows for efficient computations (fewer multiplications).
Other values like 3 or 17 are sometimes used, but they have certain security considerations.
Step 3: Create a MIME Message
def create_mime_message(subject, body):
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = "[email protected]"
msg['To'] = "[email protected]"
text_part = MIMEText(body, 'plain')
msg.attach(text_part)
return msg
This function constructs a MIME message:
○ MIMEMultipart() initializes a new multipart message.
○ The headers (Subject, From, To) are set with the provided values.
○ The body text is created as a MIMEText object and attached to the message.
Finally, the constructed message is returned.
Step 4: Sign the Message
def sign_message(message):
message_bytes = message.as_bytes()
signature = private_key.sign(
message_bytes,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
return base64.b64encode(signature).decode('utf-8')
The sign_message function signs the MIME message:
○ message.as_bytes() converts the MIME message to bytes, which is
needed for signing.
○ The private_key.sign() method is called with:
■ The message bytes.
■ A padding scheme (PSS) that provides security against certain types
of attacks.
■ A hash function (SHA-256) to create a digest of the message content.
The resulting signature is encoded in Base64 for easy transmission and returned as a string.
Step 5: Create and Sign the MIME Message
subject = "Test Email"
body = "This is a test email with a digital signature."
mime_message = create_mime_message(subject, body)
signature = sign_message(mime_message)
print("MIME Message:")
print(mime_message.as_string())
print("\nDigital Signature (Base64):")
print(signature)
Here, we set the subject and body for the email, create the MIME message using
create_mime_message, and sign it with sign_message.
The MIME message and the Base64-encoded signature are printed.
Step 6: Verify the Signature
def verify_signature(message, signature):
message_bytes = message.as_bytes()
signature_bytes = base64.b64decode(signature)
try:
public_key.verify(
signature_bytes,
message_bytes,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
return True
except Exception as e:
print(f"Verification failed: {e}")
return False
This function checks the validity of the signature:
○ Converts the message to bytes.
○ Decodes the Base64 signature back to bytes.
○ Calls public_key.verify() with:
■ The signature bytes.
■ The original message bytes.
■ The same padding and hash function used during signing.
If the verification passes, it returns True; otherwise, it catches the exception and prints an
error message.
Step 7: Check the Signature
is_valid = verify_signature(mime_message, signature)
print("\nSignature Valid:", is_valid)
Finally, the signature is verified using the public key, and the result is printed to indicate
whether the signature is valid.