manage-your-printer/utils/ssl_manager.py
2025-06-04 10:03:22 +02:00

270 lines
10 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SSL-Manager für die MYP-Plattform
Generiert und verwaltet SSL-Zertifikate für Mercedes-Benz Yard Printing
"""
import os
import socket
from datetime import datetime, timedelta
from cryptography import x509
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
import ipaddress
class SSLManager:
"""SSL-Zertifikat-Manager für die MYP-Plattform"""
def __init__(self, cert_path: str = None, key_path: str = None):
"""
Initialisiert den SSL-Manager
Args:
cert_path: Pfad zum SSL-Zertifikat
key_path: Pfad zum SSL-Schlüssel
"""
from config.settings import SSL_CERT_PATH, SSL_KEY_PATH
self.cert_path = cert_path or SSL_CERT_PATH
self.key_path = key_path or SSL_KEY_PATH
# Stelle sicher, dass das Verzeichnis existiert
cert_dir = os.path.dirname(self.cert_path)
if not os.path.exists(cert_dir):
os.makedirs(cert_dir, exist_ok=True)
def generate_mercedes_certificate(self,
hostname: str = "localhost",
validity_days: int = 365) -> bool:
"""
Generiert ein Mercedes-Benz SSL-Zertifikat
Args:
hostname: Hostname für das Zertifikat
validity_days: Gültigkeitsdauer in Tagen
Returns:
bool: True wenn erfolgreich, False bei Fehler
"""
try:
print(f"Generiere Mercedes-Benz SSL-Zertifikat für {hostname}...")
# Privaten Schlüssel generieren (4096-bit für höhere Sicherheit)
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=4096,
)
# Subject und Issuer für Mercedes-Benz
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "DE"),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Baden-Württemberg"),
x509.NameAttribute(NameOID.LOCALITY_NAME, "Stuttgart"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Mercedes-Benz Group AG"),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "IT Infrastructure"),
x509.NameAttribute(NameOID.COMMON_NAME, hostname),
x509.NameAttribute(NameOID.EMAIL_ADDRESS, "admin@mercedes-benz.com"),
])
# Zertifikat erstellen
cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
issuer
).public_key(
private_key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.utcnow()
).not_valid_after(
datetime.utcnow() + timedelta(days=validity_days)
)
# Subject Alternative Names hinzufügen
san_list = [
x509.DNSName(hostname),
x509.DNSName("localhost"),
x509.DNSName("*.localhost"),
x509.DNSName("raspberrypi"),
x509.DNSName("*.raspberrypi"),
x509.DNSName("myp.mercedes-benz.local"),
x509.DNSName("*.myp.mercedes-benz.local"),
x509.IPAddress(ipaddress.IPv4Address("127.0.0.1")),
x509.IPAddress(ipaddress.IPv4Address("0.0.0.0")),
]
# Lokale IP-Adresse hinzufügen
try:
local_ip = socket.gethostbyname(socket.gethostname())
if local_ip and local_ip != "127.0.0.1":
san_list.append(x509.IPAddress(ipaddress.IPv4Address(local_ip)))
except:
pass
cert = cert.add_extension(
x509.SubjectAlternativeName(san_list),
critical=False,
)
# Key Usage Extension
cert = cert.add_extension(
x509.KeyUsage(
digital_signature=True,
key_encipherment=True,
key_agreement=False,
key_cert_sign=False,
crl_sign=False,
content_commitment=False,
data_encipherment=False,
encipher_only=False,
decipher_only=False,
),
critical=True,
)
# Extended Key Usage
cert = cert.add_extension(
x509.ExtendedKeyUsage([
ExtendedKeyUsageOID.SERVER_AUTH,
ExtendedKeyUsageOID.CLIENT_AUTH,
]),
critical=True,
)
# Basic Constraints
cert = cert.add_extension(
x509.BasicConstraints(ca=False, path_length=None),
critical=True,
)
# Zertifikat signieren
cert = cert.sign(private_key, hashes.SHA256())
# Zertifikat in Datei schreiben
with open(self.cert_path, "wb") as f:
f.write(cert.public_bytes(serialization.Encoding.PEM))
# Privaten Schlüssel in Datei schreiben
with open(self.key_path, "wb") as f:
f.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
))
print(f"✓ SSL-Zertifikat erfolgreich erstellt: {self.cert_path}")
print(f"✓ SSL-Schlüssel erfolgreich erstellt: {self.key_path}")
# Zertifikatsinformationen anzeigen
self._print_certificate_info(cert)
return True
except Exception as e:
print(f"✗ Fehler beim Erstellen des SSL-Zertifikats: {e}")
return False
def _print_certificate_info(self, cert):
"""Zeigt Informationen über das erstellte Zertifikat an"""
try:
print("\n=== Zertifikatsinformationen ===")
print(f"Subject: {cert.subject.rfc4514_string()}")
print(f"Gültig von: {cert.not_valid_before}")
print(f"Gültig bis: {cert.not_valid_after}")
print(f"Seriennummer: {cert.serial_number}")
# SAN anzeigen
try:
san_ext = cert.extensions.get_extension_for_oid(x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
print("Subject Alternative Names:")
for name in san_ext.value:
print(f" - {name}")
except:
pass
print("================================\n")
except Exception as e:
print(f"Fehler beim Anzeigen der Zertifikatsinformationen: {e}")
def certificate_exists(self) -> bool:
"""
Prüft, ob SSL-Zertifikat und Schlüssel existieren
Returns:
bool: True wenn beide Dateien existieren
"""
return os.path.exists(self.cert_path) and os.path.exists(self.key_path)
def get_certificate_info(self) -> dict:
"""
Gibt Informationen über das vorhandene Zertifikat zurück
Returns:
dict: Zertifikatsinformationen oder None bei Fehler
"""
if not self.certificate_exists():
return None
try:
with open(self.cert_path, "rb") as f:
cert_data = f.read()
cert = x509.load_pem_x509_certificate(cert_data)
return {
"subject": cert.subject.rfc4514_string(),
"issuer": cert.issuer.rfc4514_string(),
"not_valid_before": cert.not_valid_before,
"not_valid_after": cert.not_valid_after,
"serial_number": cert.serial_number,
"is_expired": datetime.utcnow() > cert.not_valid_after,
"days_until_expiry": (cert.not_valid_after - datetime.utcnow()).days
}
except Exception as e:
print(f"Fehler beim Lesen der Zertifikatsinformationen: {e}")
return None
# Globale SSL-Manager-Instanz
ssl_manager = SSLManager()
def ensure_ssl_certificates() -> bool:
"""
Stellt sicher, dass SSL-Zertifikate vorhanden sind
Returns:
bool: True wenn Zertifikate verfügbar sind
"""
if ssl_manager.certificate_exists():
cert_info = ssl_manager.get_certificate_info()
if cert_info and not cert_info["is_expired"]:
print(f"✓ Gültiges SSL-Zertifikat gefunden (läuft ab in {cert_info['days_until_expiry']} Tagen)")
return True
else:
print("⚠ SSL-Zertifikat ist abgelaufen, erstelle neues...")
print("SSL-Zertifikate nicht gefunden, erstelle neue...")
return ssl_manager.generate_mercedes_certificate()
if __name__ == "__main__":
# Direkte Ausführung für Tests
print("Mercedes-Benz SSL-Zertifikat-Generator")
print("=====================================")
if ssl_manager.certificate_exists():
print("Vorhandene Zertifikate gefunden:")
info = ssl_manager.get_certificate_info()
if info:
print(f" Subject: {info['subject']}")
print(f" Gültig bis: {info['not_valid_after']}")
print(f" Status: {'Abgelaufen' if info['is_expired'] else 'Gültig'}")
success = ssl_manager.generate_mercedes_certificate()
if success:
print("✓ SSL-Zertifikat erfolgreich generiert!")
else:
print("✗ Fehler beim Generieren des SSL-Zertifikats!")