PRACTICE LAB 5

Cryptography Implementation & PKI Lab

Implement AES-256 file encryption libraries, generate RSA custom key pairs, establish a local self-signed Certification Authority (CA), sign TLS server certificates, and configure HTTPS servers.

Environment
OpenSSL / Python shell
Difficulty
Beginner (Level 3)
Course Module
Cryptography
Deliverables
AES script, TLS Keys, and HTTPS Padlock
1. System Architecture & Workflow

The diagram below shows the Public Key Infrastructure (PKI) trust chain we will build. A local Certificate Authority (CA) generates a root private key and self-signed certificate. This CA then signs a server certificate used by a local HTTPS web server. Finally, the CA root certificate is imported into the browser's trust store so it recognises the server's identity and displays the green padlock.

Architecture Diagram
2. Part 1: Step-by-Step Action Items & Key Execution Steps
STEP 1

Create the Project Directory

First, create a dedicated folder for all your cryptography files — keys, certificates, and scripts.

  1. Open your Kali Linux VM inside VirtualBox. Wait for the desktop to fully load.
  2. Click on the Terminal Emulator icon in the top taskbar (black rectangular screen icon). A terminal window opens.
  3. Create a new project directory:
$ mkdir -p ~/crypto-lab && cd ~/crypto-lab
This creates a new folder called crypto-lab in your home directory and immediately moves into it. The && means "run the second command only if the first succeeds".
PartWhat It Does
mkdir -pCreates the directory; -p prevents errors if it already exists
~/crypto-lab~ = your home folder. Creates /home/kali/crypto-lab
&&Runs the next command only if mkdir succeeded
cd ~/crypto-labChanges your current working directory into the new folder
What you should see: Your terminal prompt now shows ~/crypto-lab$, confirming you are inside the project directory.
STEP 2

Generate the Root CA Private Key

A Certificate Authority (CA) needs a private key to sign certificates. This key is the foundation of your entire PKI trust chain — anyone who has this key can issue trusted certificates.

  1. Make sure you are inside the ~/crypto-lab directory (check your terminal prompt).
  2. Type the following command and press Enter:
$ openssl genrsa -out ca.key 4096
This generates a 4096-bit RSA private key and saves it to a file called ca.key. RSA is an asymmetric encryption algorithm — it creates a mathematically-linked pair of keys (public and private). The 4096-bit size makes it extremely difficult to crack.
PartWhat It Does
opensslThe OpenSSL command-line tool for cryptographic operations
genrsaGenerate RSA — tells OpenSSL to create a new RSA private key
-out ca.keySaves the generated key to a file named ca.key
4096The key length in bits. 4096 bits is very strong (2048 is minimum recommended)
What you should see: Output like Generating RSA private key, 4096 bit long modulus followed by dots and plus signs showing the key generation progress. When done, the file ca.key is created.
  1. Verify the key was created by listing files:
$ ls -la ca.key
Lists the file details. You should see ca.key with a size of approximately 3.2 KB. The -la flags show detailed info (-l) and include hidden files (-a).
STEP 3

Generate the Root CA Self-Signed Certificate

Now use the private key to create a self-signed certificate. This certificate identifies your CA and will be used to sign other certificates.

  1. Type the following command and press Enter:
$ openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=LocalSecurityCA"
This creates a new self-signed X.509 certificate using your CA private key, with SHA-256 hashing, valid for 365 days, and sets the Common Name to "LocalSecurityCA".
FlagWhat It Does
reqThe OpenSSL certificate request subcommand
-x509Output a self-signed certificate instead of a certificate signing request (CSR). X.509 is the standard format for digital certificates
-newGenerate a new certificate request
-nodesNo DES — do not encrypt the private key with a passphrase (makes it easier for automated scripts to use)
-key ca.keyUse the CA private key we just generated to sign the certificate
-sha256Use the SHA-256 hashing algorithm for the certificate signature (secure and widely supported)
-days 365The certificate is valid for 365 days (1 year) from today
-out ca.crtSave the certificate to a file called ca.crt
-subj "/CN=LocalSecurityCA"Sets the subject fields. CN = Common Name, which identifies this certificate as "LocalSecurityCA". This avoids interactive prompts
What you should see: No output if successful (silence means success in OpenSSL). The file ca.crt is now created. Run ls to confirm both ca.key and ca.crt exist.
  1. Inspect the certificate contents to verify it was created correctly:
$ openssl x509 -in ca.crt -text -noout | head -20
This displays the certificate details in human-readable text format. The -noout flag prevents printing the raw base64, and head -20 shows only the first 20 lines.
What you should see: Certificate details showing Issuer: CN = LocalSecurityCA, Subject: CN = LocalSecurityCA (same because it's self-signed), Validity dates, and the SHA-256 signature algorithm.
STEP 4

Generate the Web Server Private Key

Now create a separate private key for the web server. This key is different from the CA key — the server uses its own key pair for TLS encryption.

  1. Type the following command and press Enter:
$ openssl genrsa -out server.key 2048
This generates a 2048-bit RSA private key for the web server. We use 2048 bits here (instead of 4096) because server keys are rotated more frequently and 2048 bits provides sufficient security with better performance.
PartWhat It Does
genrsaGenerate a new RSA private key
-out server.keySave the key to server.key
2048Key size in bits — standard for web server TLS certificates
STEP 5

Create a Certificate Signing Request (CSR)

A CSR is a formal request asking the CA to sign a certificate for the server. It contains the server's public key and identity information.

  1. Type the following command and press Enter:
$ openssl req -new -key server.key -out server.csr -subj "/CN=localhost"
This creates a Certificate Signing Request using the server's private key. The Common Name is set to localhost because our web server will run locally — this must match the domain name you'll access in the browser.
FlagWhat It Does
req -newCreate a new certificate signing request
-key server.keyUse the server's private key (the CSR will contain the corresponding public key)
-out server.csrSave the CSR to server.csr
-subj "/CN=localhost"Set the Common Name to localhost. This must match the URL you'll visit in the browser, otherwise the browser shows a certificate mismatch error
STEP 6

Sign the Server Certificate with the CA

Now the CA signs the server's CSR, producing the final server certificate. This is the core of PKI — the CA vouches for the server's identity by signing its certificate.

  1. Type the following command and press Enter:
$ openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365 -sha256
This signs the server's certificate request using the CA's certificate and private key, producing the final signed server certificate valid for 1 year.
FlagWhat It Does
x509 -reqProcess a certificate signing request (the CSR we just created)
-in server.csrInput: the server's CSR file
-CA ca.crtThe CA certificate to use for signing
-CAkey ca.keyThe CA's private key to create the digital signature
-CAcreateserialAutomatically create a serial number file (tracks issued certificates)
-out server.crtSave the signed server certificate to server.crt
-days 365Certificate validity period: 365 days
-sha256Use SHA-256 for the signature hash algorithm
What you should see: Output showing Signature ok and subject details. The file server.crt is created.
  1. Verify the certificate chain by running:
$ openssl verify -CAfile ca.crt server.crt
This verifies that server.crt was genuinely signed by the CA whose certificate is ca.crt. It validates the entire trust chain.
What you should see: server.crt: OK — this confirms the certificate chain is valid and the server cert was properly signed by the CA.
  1. List all generated files to confirm everything exists:
$ ls -la
What you should see: Five files: ca.key, ca.crt, ca.srl, server.key, server.csr, server.crt.
STEP 7

Import the CA Root Certificate into Firefox Trust Store

For the browser to trust your locally-signed server certificate, you need to import your CA's root certificate into the browser's trusted certificate store.

  1. Open Firefox browser (click the Firefox icon in the taskbar).
  2. Click the hamburger menu (☰ three horizontal lines) in the top-right corner of Firefox.
  3. Click "Settings" from the dropdown menu.
  4. In the left sidebar, click on "Privacy & Security".
  5. Scroll down to the "Certificates" section (near the bottom of the page).
  6. Click the "View Certificates..." button. A Certificate Manager window opens.
  7. Click on the "Authorities" tab at the top of the Certificate Manager window.
  8. Click the "Import..." button at the bottom.
  9. A file browser dialog opens. Navigate to your Home folder → crypto-lab folder.
  10. Select the ca.crt file and click "Open".
  11. A trust dialog appears. Check the box "Trust this CA to identify websites".
  12. Click "OK" to confirm the import.
  13. You should now see "LocalSecurityCA" listed in the Authorities tab. Click "OK" to close the Certificate Manager.
What you should see: "LocalSecurityCA" is now listed under the Authorities tab in Firefox's Certificate Manager. Firefox will now trust any certificate signed by this CA.
3. Automation Architecture

The diagram below shows the complete PKI certificate lifecycle pipeline: from generating the CA root key and self-signed certificate, to creating a server CSR, signing it with the CA, launching a TLS-enabled Python HTTPS server, and importing the CA root into the browser trust store.

Automation Flow Diagram
4. Part 2: Code Breakdown — Line by Line

Below is the Python HTTPS server script secure_server.py. We will explain each line individually, then show the complete combined script at the end.

Line-by-Line Explanation

# secure_server.py - Python TLS HTTPS Server Engine
Line 1 — Comment: A comment describing the script's purpose. The # tells Python to ignore this line. This is a TLS-enabled HTTPS server using Python's built-in modules.
import http.server
Line 2 — Import http.server: Imports Python's built-in HTTP server module. This provides HTTPServer (a basic web server class) and SimpleHTTPRequestHandler (handles incoming HTTP requests by serving files from the current directory). We'll wrap this with SSL to make it HTTPS.
import ssl
Line 3 — Import ssl: Imports Python's SSL/TLS module, which provides the ability to wrap network connections with encryption. SSL (Secure Sockets Layer) and its successor TLS (Transport Layer Security) encrypt data in transit between client and server.
def run_server():
Line 4 — Function Definition: Defines a function called run_server that contains all the server setup logic. Using a function keeps the code organized and reusable.
server_address = ('localhost', 4433)
Line 5 — Server Address: Creates a tuple defining where the server will listen. 'localhost' means it only accepts connections from your own machine (127.0.0.1). 4433 is the port number — we use 4433 instead of the standard HTTPS port 443 because port 443 requires root/admin privileges.
httpd = http.server.HTTPServer(server_address, http.server.SimpleHTTPRequestHandler)
Line 6 — Create HTTP Server: Creates a new HTTP server instance bound to our address and port. HTTPServer handles listening for connections, and SimpleHTTPRequestHandler serves files from the current directory. When someone visits the URL, they see a directory listing of files.
# Wrap socket with SSL context
Line 7 — Comment: Indicates the next lines add TLS encryption to the server's network socket, upgrading it from plain HTTP to encrypted HTTPS.
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Line 8 — Create SSL Context: Creates an SSL context object configured for a TLS server. The ssl.PROTOCOL_TLS_SERVER constant tells Python to use the latest TLS protocol version for server-side connections. The context holds all the TLS configuration (certificates, keys, protocol settings).
context.load_cert_chain(certfile="server.crt", keyfile="server.key")
Line 9 — Load Certificate Chain: Loads the server's certificate (server.crt) and private key (server.key) into the SSL context. The certificate is sent to clients during the TLS handshake to prove the server's identity. The private key is used to decrypt data encrypted with the server's public key.
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
Line 10 — Wrap Socket with TLS: Replaces the server's plain TCP socket with a TLS-encrypted socket. wrap_socket() adds the encryption layer on top of the existing connection. server_side=True tells Python this is the server end of the connection (not a client).
print("[STATUS] Local secure TLS server listening on https://localhost:4433...")
Line 11 — Status Message: Prints a confirmation message showing the HTTPS URL where the server is running. Users can visit this URL in their browser.
httpd.serve_forever()
Line 12 — Start Serving: Starts the server in an infinite loop, continuously listening for and handling incoming HTTPS requests. The server runs until you stop it with Ctrl + C.
if __name__ == "__main__":
Line 13 — Main Guard: Standard Python entry point check. Only runs the server when you execute this file directly with python secure_server.py.
run_server()
Line 14 — Function Call: Calls the run_server() function to start the HTTPS server.
✓ Complete Combined Script: Now that you understand every line, here is the full script. Create a file called secure_server.py and paste this code into it.

How to Create and Run the Script

  1. Open your Terminal and make sure you're in the ~/crypto-lab directory.
  2. Create the Python file: nano secure_server.py
  3. Copy and paste the complete script below:
Copy
# secure_server.py - Python TLS HTTPS Server Engine
import http.server
import ssl

def run_server():
    server_address = ('localhost', 4433)
    httpd = http.server.HTTPServer(server_address, http.server.SimpleHTTPRequestHandler)
    
    # Wrap socket with SSL context
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    context.load_cert_chain(certfile="server.crt", keyfile="server.key")
    
    httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
    print("[STATUS] Local secure TLS server listening on https://localhost:4433...")
    httpd.serve_forever()

if __name__ == "__main__":
    run_server()
  1. Press Ctrl + O to save, Enter to confirm, Ctrl + X to exit nano.
  2. Run the HTTPS server:
$ python secure_server.py
This starts the HTTPS server on port 4433 using the TLS certificate and key we generated. The server will serve files from the current directory over an encrypted HTTPS connection.
Expected Output:
[STATUS] Local secure TLS server listening on https://localhost:4433...
The server is now running. Do not close this terminal window.
  1. Open Firefox browser.
  2. In the address bar, type https://localhost:4433 and press Enter.
  3. If you imported the CA certificate correctly in Step 7, you should see a green padlock icon next to the URL — confirming the connection is encrypted and trusted.
  4. Click the padlock icon to inspect the certificate details. You should see "Verified by: LocalSecurityCA".
  5. To stop the server, go back to the terminal and press Ctrl + C.
5. Deliverables Summary

Created Files / Templates

  • ca.key — Root CA RSA 4096-bit private key
  • ca.crt — Self-signed root CA certificate
  • server.key — Web server RSA 2048-bit private key
  • server.csr — Certificate signing request for the server
  • server.crt — CA-signed server TLS certificate
  • secure_server.py — Python TLS HTTPS server script

Verification Artifacts

  • Browser screenshot showing green padlock on https://localhost:4433
  • Terminal output of openssl verify -CAfile ca.crt server.crt showing "OK"
  • Certificate details showing "Verified by: LocalSecurityCA"
6. Closing Explanation: Why We Did This & What It Accomplishes

Architectural Intent & Operational Impact

Why We Did This

What This Accomplishes