SSL/TLS - Certificates, HTTPS, Secure Connections

SecuritySSL/TLSFree Lesson

Advertisement

Introduction

SSL/TLS provides encryption for network communication. This tutorial covers certificate generation, HTTPS configuration, and secure connection handling in Python.

Generating Self-Signed Certificates

from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
import datetime

def generate_self_signed_cert(domain: str = "localhost"):
    """Generate a self-signed certificate for development."""
    
    # Generate private key
    key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
    )
    
    # Certificate subject
    subject = issuer = x509.Name([
        x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
        x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "California"),
        x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Development"),
        x509.NameAttribute(NameOID.COMMON_NAME, domain),
    ])
    
    # Build certificate
    cert = (
        x509.CertificateBuilder()
        .subject_name(subject)
        .issuer_name(issuer)
        .public_key(key.public_key())
        .serial_number(x509.random_serial_number())
        .not_valid_before(datetime.datetime.utcnow())
        .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365))
        .add_extension(
            x509.SubjectAlternativeName([
                x509.DNSName(domain),
                x509.DNSName("localhost"),
                x509.IPAddress(ipaddress.IPv4Address("127.0.0.1")),
            ]),
            critical=False,
        )
        .sign(key, hashes.SHA256())
    )
    
    return cert, key

# Generate and save
cert, key = generate_self_signed_cert()

# Save certificate
with open("cert.pem", "wb") as f:
    f.write(cert.public_bytes(serialization.Encoding.PEM))

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

HTTPS with Flask

# app.py
from flask import Flask
import ssl

app = Flask(__name__)

@app.route('/')
def index():
    return 'Secure Flask App!'

if __name__ == '__main__':
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    context.load_cert_chain('cert.pem', 'key.pem')
    context.set_ciphers('ECDHE+AESGCM')
    context.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
    
    app.run(ssl_context=context, port=443)

HTTPS with FastAPI

# fastapi_https.py
from fastapi import FastAPI
import uvicorn

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Secure FastAPI"}

if __name__ == '__main__':
    uvicorn.run(
        app,
        host="0.0.0.0",
        port=443,
        ssl_keyfile="key.pem",
        ssl_certfile="cert.pem"
    )

Secure Requests

import requests
import ssl
from urllib3.util.ssl_ import create_urllib3_context

def create_verified_session(verify_cert: bool = True):
    """Create a requests session with SSL verification."""
    session = requests.Session()
    
    if verify_cert:
        session.verify = True  # Use system CA bundle
    else:
        session.verify = './custom-ca-bundle.crt'
    
    return session

# Custom SSL context
def create_custom_context():
    """Create custom SSL context with strong configuration."""
    context = ssl.create_default_context()
    context.minimum_version = ssl.TLSVersion.TLSv1_2
    context.set_ciphers('ECDHE+AESGCM:ECDHE+CHACHA20')
    return context

# Using httpx
import httpx

async def secure_request(url: str):
    """Make secure HTTPS request with httpx."""
    async with httpx.AsyncClient(
        verify=True,
        timeout=30.0
    ) as client:
        response = await client.get(url)
        return response

Certificate Verification

import ssl
import socket
from cryptography import x509
from cryptography.hazmat.backends import default_backend

def verify_certificate(hostname: str, port: int = 443) -> dict:
    """Verify SSL certificate of a server."""
    context = ssl.create_default_context()
    
    with socket.create_connection((hostname, port)) as sock:
        with context.wrap_socket(sock, server_hostname=hostname) as ssock:
            cert = ssock.getpeercert(binary_form=True)
            
    # Parse certificate
    x509_cert = x509.load_der_x509_certificate(cert, default_backend())
    
    return {
        "subject": dict(x509_cert.subject),
        "issuer": dict(x509_cert.issuer),
        "not_before": x509_cert.not_valid_before_utc,
        "not_after": x509_cert.not_valid_after_utc,
        "serial_number": x509_cert.serial_number
    }

# Check certificate
result = verify_certificate("example.com")
print(f"Valid until: {result['not_after']}")

Practice Problems

  1. Create a client certificate for mutual TLS authentication
  2. Implement certificate pinning in requests
  3. Add automatic certificate renewal with ACME
  4. Configure HSTS headers for secure connections
  5. Implement OCSP stapling verification

Advertisement

Need Expert Python Help?

Get personalized tutoring, project support, or professional consulting.

Advertisement