PKI-Lab mit Root-CA und Teilnehmer-CA: Unterschied zwischen den Versionen
| (37 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
| − | =PKI | + | =PKI mit Root-CA und Sub-CA (Beispiel it213)= |
| − | In diesem | + | In diesem Beispiel wird eine einfache PKI aufgebaut. |
| − | |||
| − | |||
| − | ==Root-CA erstellen | + | Der Trainer betreibt eine Root-CA. |
| − | + | ||
| + | Die Teilnehmer erzeugen eine Sub-CA, die von der Root-CA signiert wird. | ||
| + | |||
| + | Mit dieser Sub-CA werden anschließend Server-Zertifikate signiert. | ||
| + | |||
| + | |||
| + | ==Root-CA erstellen== | ||
| + | |||
| + | Die Root-CA ist der Vertrauensanker der PKI und ist selbstsigniert. | ||
*openssl req -new -x509 -newkey rsa:4096 -nodes -keyout ca.key -out ca.crt -days 3650 -subj "/CN=Kit Root CA" | *openssl req -new -x509 -newkey rsa:4096 -nodes -keyout ca.key -out ca.crt -days 3650 -subj "/CN=Kit Root CA" | ||
| − | Das Zertifikat | + | Das Zertifikat kann anschließend kontrolliert werden. |
*openssl x509 -in ca.crt -text -noout | *openssl x509 -in ca.crt -text -noout | ||
| Zeile 16: | Zeile 22: | ||
==Root-CA auf Clients installieren== | ==Root-CA auf Clients installieren== | ||
| − | Damit Clients Zertifikaten vertrauen, | + | |
| − | muss die Root-CA im Trust-Store | + | Damit Clients den Zertifikaten vertrauen, muss die Root-CA im Trust-Store installiert werden. |
| + | |||
===Debian=== | ===Debian=== | ||
| + | |||
Das Root-Zertifikat wird in den lokalen CA-Speicher kopiert. | Das Root-Zertifikat wird in den lokalen CA-Speicher kopiert. | ||
| Zeile 30: | Zeile 38: | ||
===Rocky / RHEL=== | ===Rocky / RHEL=== | ||
| + | |||
Das Root-Zertifikat wird in den Anchor-Store kopiert. | Das Root-Zertifikat wird in den Anchor-Store kopiert. | ||
| Zeile 37: | Zeile 46: | ||
*update-ca-trust extract | *update-ca-trust extract | ||
| + | === Firefox / Chrome / Mozilla === | ||
| + | ;Müssen getrennt importiert werden. | ||
| + | ==Sub-CA Request erzeugen (Beispiel it213)== | ||
| − | + | Der Teilnehmer erzeugt einen privaten Schlüssel und eine Certificate Signing Request für seine CA. | |
| − | Der Teilnehmer erzeugt einen privaten Schlüssel und eine Certificate Signing Request | + | *mkdir intermediata-ca |
| − | für seine | + | *cd intermediata-ca |
| − | |||
*openssl req -new -newkey rsa:4096 -nodes -keyout it213-ca.key -out it213-ca.csr -subj "/CN=it213 Lab CA" | *openssl req -new -newkey rsa:4096 -nodes -keyout it213-ca.key -out it213-ca.csr -subj "/CN=it213 Lab CA" | ||
| + | ==Der Certificate Signing Request muss nun zur Zertifizierungsstelle== | ||
| + | *http://192.168.9.88 | ||
| + | ==Sub-CA durch Root signieren== | ||
| − | |||
Die Root-CA signiert die Teilnehmer-CA. Dadurch entsteht die Zertifikatskette. | Die Root-CA signiert die Teilnehmer-CA. Dadurch entsteht die Zertifikatskette. | ||
| − | *openssl x509 -req -in it213-ca.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out it213-ca.crt -days | + | *openssl x509 -req -in it213-ca.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out it213-ca.crt -days 1460 -extfile <(printf "basicConstraints=CA:TRUE\nkeyUsage=keyCertSign,cRLSign") |
| + | |||
| + | Das signierte CA-Zertifikat kann kontrolliert werden. | ||
| + | *openssl x509 -in it213-ca.crt -text -noout | ||
| − | == | + | ==Die Zertifikate können nun hier runtergeladen werden== |
| − | + | *http://192.168.9.88/uploads | |
| − | + | ||
| + | ==Hinweis zum privaten Schlüssel== | ||
| + | |||
| + | Der private Schlüssel der CA wird nur zum Signieren von Zertifikaten benötigt. | ||
| + | |||
| + | Zum Prüfen der Zertifikatskette werden nur die öffentlichen Zertifikate verwendet. | ||
| + | |||
| + | Beispiel: | ||
| + | |||
| + | <pre> | ||
| + | www.it213.int.crt | ||
| + | it213-ca.crt | ||
| + | ca.crt | ||
| + | </pre> | ||
| + | |||
| + | Die Prüfung erfolgt über die Signaturen der Zertifikate. | ||
==Server-Schlüssel und CSR erzeugen== | ==Server-Schlüssel und CSR erzeugen== | ||
| − | Der Teilnehmer erzeugt einen | + | ;Variable setzen |
| + | *CA=it213-ca | ||
| + | *FQDN=www.it213.int | ||
| + | Der Teilnehmer erzeugt einen Schlüssel und eine CSR für seinen Server. | ||
| + | ;Erstellen | ||
| + | *openssl req -new -newkey rsa:2048 -nodes -keyout $FQDN.key -out $FQDN.csr -subj "/CN=$FQDN" | ||
| + | ;Anzeigen | ||
| + | *openssl req -in $FQDN.csr -text -noout | ||
| + | |||
| + | ==Server-Zertifikat signieren== | ||
| + | |||
| + | Die Sub-CA signiert das Server-Zertifikat und fügt einen Subject Alternative Name hinzu. | ||
| + | ;Signieren | ||
| + | *openssl x509 -req -in $FQDN.csr -CA $CA.crt -CAkey $CA.key -CAcreateserial -out $FQDN.crt -days 365 -extfile <(printf "subjectAltName=DNS:$FQDN") | ||
| − | *openssl | + | ;Anzeigen |
| + | *openssl x509 -in $FQDN.crt -text -noout | ||
| + | ==Fullchain erstellen== | ||
| − | + | Damit Clients die Zertifikatskette aufbauen können, muss der Server die Intermediate-CA mitliefern. | |
| − | |||
| − | + | Dazu wird eine Fullchain-Datei erstellt. | |
| + | *cat $FQDN.crt $CA.crt > $FQDN-fullchain.pem | ||
| − | + | Die Reihenfolge ist wichtig. | |
| − | |||
| − | + | <pre> | |
| + | www.it213.int.crt | ||
| + | it213-ca.crt | ||
| + | </pre> | ||
| + | ==Zum Ziel kopieren== | ||
| + | *scp $FQDN-fullchain.pem $FQDN.key kit@$FQDN: | ||
==Zertifikatskette prüfen== | ==Zertifikatskette prüfen== | ||
| − | |||
| − | *openssl verify -CAfile ca.crt -untrusted it213-ca.crt | + | Die komplette Zertifikatskette kann mit OpenSSL überprüft werden. |
| + | |||
| + | *openssl verify -CAfile ca.crt -untrusted it213-ca.crt www.it213.int.crt | ||
| + | |||
| + | ==PKI-Struktur== | ||
| − | + | <pre> | |
Kit Root CA | Kit Root CA | ||
| − | |||
| − | |||
| − | |||
| − | |||
└── it213-ca | └── it213-ca | ||
| − | └── | + | └── www.it213.int |
| + | </pre> | ||
| + | |||
| + | Der Client kennt die Root-CA aus dem Trust-Store. | ||
| + | |||
| + | Der Server liefert beim TLS-Handshake das Server-Zertifikat und die Sub-CA. | ||
| + | |||
| + | Der Client kann damit die vollständige Zertifikatskette prüfen. | ||
| + | |||
| + | == Troubleshooting: OpenSSL & PKI Fehler == | ||
| + | |||
| + | {| class="wikitable" | ||
| + | ! Fehlermeldung / Symptom !! Mögliche Ursache !! Lösung | ||
| + | |- | ||
| + | | '''"Self-signed certificate in certificate chain"''' | ||
| + | | Der Client vertraut der Root-CA nicht oder die Root-CA wurde nicht im Trust-Store installiert. | ||
| + | | Prüfen, ob '''update-ca-certificates''' (Debian) oder '''update-ca-trust''' (Rocky) ausgeführt wurde. Browser ggf. neu starten. | ||
| + | |- | ||
| + | | '''"Depth lookup: unable to get local issuer certificate"''' | ||
| + | | Die Intermediate-CA (Sub-CA) fehlt in der Kette, die der Server ausliefert. | ||
| + | | Prüfen, ob die Datei '''fullchain.pem''' korrekt erstellt wurde (Reihenfolge!) und im Webserver (SSLCertificateFile / ssl_certificate) eingebunden ist. | ||
| + | |- | ||
| + | | '''"Certificate is not valid for 'xyz.int'"''' | ||
| + | | Der '''Subject Alternative Name (SAN)''' fehlt oder ist falsch geschrieben. | ||
| + | | Zertifikat prüfen mit: <nowiki>openssl x509 -in cert.crt -text</nowiki> -> Suche nach "X509v3 Subject Alternative Name". | ||
| + | |- | ||
| + | | '''"Verification error: certificate has expired"''' | ||
| + | | Systemzeit auf Server oder Client ist falsch (häufig bei VMs nach Suspend). | ||
| + | | Datum/Uhrzeit mit dem Befehl <nowiki>date</nowiki> prüfen und ggf. per NTP synchronisieren. | ||
| + | |- | ||
| + | | '''"Modulus mismatch"''' | ||
| + | | Der private Schlüssel passt kryptografisch nicht zum vorliegenden Zertifikat. | ||
| + | | Vergleichen der MD5-Hashes beider Dateien (siehe unten unter "Nützliche Befehle"). | ||
| + | |- | ||
| + | | '''"CA:FALSE" bei der Sub-CA''' | ||
| + | | Die Sub-CA wurde ohne die Extension '''basicConstraints=CA:TRUE''' signiert. | ||
| + | | Die Sub-CA ist nicht berechtigt, weitere Zertifikate zu signieren. Sub-CA mit den richtigen Extensions neu erstellen. | ||
| + | |} | ||
| + | |||
| + | == Nützliche Prüfbefehle für die Administration == | ||
| + | |||
| + | === Modulus-Check (Passen Key und CRT zusammen?) === | ||
| + | Um sicherzustellen, dass ein Zertifikat zu einem Private Key gehört, müssen die Modulus-Hashes identisch sein: | ||
| + | * <pre>openssl x509 -noout -modulus -in www.it213.int.crt | openssl md5</pre> | ||
| + | * <pre>openssl rsa -noout -modulus -in www.it213.int.key | openssl md5</pre> | ||
| + | |||
| + | === TLS-Handshake live testen === | ||
| + | Simuliert einen Verbindungsaufbau und zeigt die vom Server gesendete Zertifikatskette an: | ||
| + | * <pre>openssl s_client -connect localhost:443 -showcerts</pre> | ||
| + | |||
| + | === Zertifikatsinhalt schnell prüfen === | ||
| + | * <pre>openssl x509 -in fullchain.pem -text -noout</pre> | ||
| + | |||
| + | |||
| + | = Python Webserver mit SSL/TLS = | ||
| + | |||
| + | In dieser Anleitung wird ein einfacher HTTPS-Webserver mit Python erstellt, der statische Inhalte aus einem Verzeichnis ausliefert. | ||
| + | = Als erstes müssen wir die fullchain.pem und den privaten Schlüssl zu Server bringen= | ||
| + | *scp fullchain.pem www.it213.int.key www: | ||
| + | |||
| + | = Auf dem Webserver= | ||
| + | *ssh kit@www | ||
| + | == Verzeichnis erstellen == | ||
| + | |||
| + | Zuerst wird ein Arbeitsverzeichnis für die Webinhalte und das Server-Skript angelegt. | ||
| + | |||
| + | * mkdir html | ||
| + | |||
| + | == Keys anpassen == | ||
| + | |||
| + | Die Zertifikatsdateien müssen in das Verzeichnis kopiert und für das Skript passend benannt werden. | ||
| + | |||
| + | * cp fullchain.pem html/own.crt | ||
| + | * cp www.it213.int.key html/own.key | ||
| + | |||
| + | == Konfiguration == | ||
| + | |||
| + | Das Python-Skript konfiguriert den Server so, dass er auf Port 8443 lauscht und die SSL-Verschlüsselung nutzt. | ||
| + | |||
| + | * nano html/https.py | ||
| + | |||
| + | <pre> | ||
| + | import http.server, ssl | ||
| + | |||
| + | server_address = ('0.0.0.0', 8443) | ||
| + | handler = http.server.SimpleHTTPRequestHandler | ||
| + | httpd = http.server.HTTPServer(server_address, handler) | ||
| + | |||
| + | context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) | ||
| + | context.load_cert_chain(certfile="own.crt", keyfile="own.key") | ||
| + | |||
| + | httpd.socket = context.wrap_socket(httpd.socket, server_side=True) | ||
| + | |||
| + | print("HTTPS läuft auf https://0.0.0.0:8443") | ||
| + | httpd.serve_forever() | ||
| + | </pre> | ||
| + | |||
| + | == Statische Seite erstellen == | ||
| + | |||
| + | Damit der Server eine Seite anzeigt, wird eine index.html im Verzeichnis erstellt. | ||
| + | |||
| + | * nano html/index.html | ||
| + | |||
| + | <pre> | ||
| + | <html> | ||
| + | <head> | ||
| + | <title>IT213 Testseite</title> | ||
| + | </head> | ||
| + | <body> | ||
| + | <h1>Erfolg!</h1> | ||
| + | <p>Der verschlüsselte Python-Webserver funktioniert und liefert diese Seite aus.</p> | ||
| + | </body> | ||
| + | </html> | ||
| + | </pre> | ||
| + | |||
| + | == Starten == | ||
| + | |||
| + | Um den Server zu starten, wechselt man in das Verzeichnis und führt das Skript aus. | ||
| + | |||
| + | * cd html | ||
| + | * python3 https.py | ||
| − | + | Der Zugriff erfolgt im Browser über https://www.it213.int:8443. | |
| − | + | == Tests vom Client == | |
| + | ;Verschlüsselter Verbindungsaufbau | ||
| + | *openssl s_client -host www.it213.int -port 8443 | ||
| + | ;Welche Zertifikate werden angeboten? | ||
| + | *nmap --script ssl-cert www.it213.int -p 8443 | ||
| + | ;Welche SSL/TLS Versionen werden angeboten | ||
| + | *nmap --script ssl-enum-ciphers www.it213.int -p 8443 | ||
Aktuelle Version vom 19. März 2026, 13:49 Uhr
PKI mit Root-CA und Sub-CA (Beispiel it213)
In diesem Beispiel wird eine einfache PKI aufgebaut.
Der Trainer betreibt eine Root-CA.
Die Teilnehmer erzeugen eine Sub-CA, die von der Root-CA signiert wird.
Mit dieser Sub-CA werden anschließend Server-Zertifikate signiert.
Root-CA erstellen
Die Root-CA ist der Vertrauensanker der PKI und ist selbstsigniert.
- openssl req -new -x509 -newkey rsa:4096 -nodes -keyout ca.key -out ca.crt -days 3650 -subj "/CN=Kit Root CA"
Das Zertifikat kann anschließend kontrolliert werden.
- openssl x509 -in ca.crt -text -noout
Root-CA auf Clients installieren
Damit Clients den Zertifikaten vertrauen, muss die Root-CA im Trust-Store installiert werden.
Debian
Das Root-Zertifikat wird in den lokalen CA-Speicher kopiert.
- cp ca.crt /usr/local/share/ca-certificates/
Der Trust-Store wird aktualisiert.
- update-ca-certificates
Rocky / RHEL
Das Root-Zertifikat wird in den Anchor-Store kopiert.
- cp ca.crt /etc/pki/ca-trust/source/anchors/
Der System Trust Store wird neu erzeugt.
- update-ca-trust extract
Firefox / Chrome / Mozilla
- Müssen getrennt importiert werden.
Sub-CA Request erzeugen (Beispiel it213)
Der Teilnehmer erzeugt einen privaten Schlüssel und eine Certificate Signing Request für seine CA.
- mkdir intermediata-ca
- cd intermediata-ca
- openssl req -new -newkey rsa:4096 -nodes -keyout it213-ca.key -out it213-ca.csr -subj "/CN=it213 Lab CA"
Der Certificate Signing Request muss nun zur Zertifizierungsstelle
Sub-CA durch Root signieren
Die Root-CA signiert die Teilnehmer-CA. Dadurch entsteht die Zertifikatskette.
- openssl x509 -req -in it213-ca.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out it213-ca.crt -days 1460 -extfile <(printf "basicConstraints=CA:TRUE\nkeyUsage=keyCertSign,cRLSign")
Das signierte CA-Zertifikat kann kontrolliert werden.
- openssl x509 -in it213-ca.crt -text -noout
Die Zertifikate können nun hier runtergeladen werden
Hinweis zum privaten Schlüssel
Der private Schlüssel der CA wird nur zum Signieren von Zertifikaten benötigt.
Zum Prüfen der Zertifikatskette werden nur die öffentlichen Zertifikate verwendet.
Beispiel:
www.it213.int.crt it213-ca.crt ca.crt
Die Prüfung erfolgt über die Signaturen der Zertifikate.
Server-Schlüssel und CSR erzeugen
- Variable setzen
- CA=it213-ca
- FQDN=www.it213.int
Der Teilnehmer erzeugt einen Schlüssel und eine CSR für seinen Server.
- Erstellen
- openssl req -new -newkey rsa:2048 -nodes -keyout $FQDN.key -out $FQDN.csr -subj "/CN=$FQDN"
- Anzeigen
- openssl req -in $FQDN.csr -text -noout
Server-Zertifikat signieren
Die Sub-CA signiert das Server-Zertifikat und fügt einen Subject Alternative Name hinzu.
- Signieren
- openssl x509 -req -in $FQDN.csr -CA $CA.crt -CAkey $CA.key -CAcreateserial -out $FQDN.crt -days 365 -extfile <(printf "subjectAltName=DNS:$FQDN")
- Anzeigen
- openssl x509 -in $FQDN.crt -text -noout
Fullchain erstellen
Damit Clients die Zertifikatskette aufbauen können, muss der Server die Intermediate-CA mitliefern.
Dazu wird eine Fullchain-Datei erstellt.
- cat $FQDN.crt $CA.crt > $FQDN-fullchain.pem
Die Reihenfolge ist wichtig.
www.it213.int.crt it213-ca.crt
Zum Ziel kopieren
- scp $FQDN-fullchain.pem $FQDN.key kit@$FQDN:
Zertifikatskette prüfen
Die komplette Zertifikatskette kann mit OpenSSL überprüft werden.
- openssl verify -CAfile ca.crt -untrusted it213-ca.crt www.it213.int.crt
PKI-Struktur
Kit Root CA
└── it213-ca
└── www.it213.int
Der Client kennt die Root-CA aus dem Trust-Store.
Der Server liefert beim TLS-Handshake das Server-Zertifikat und die Sub-CA.
Der Client kann damit die vollständige Zertifikatskette prüfen.
Troubleshooting: OpenSSL & PKI Fehler
| Fehlermeldung / Symptom | Mögliche Ursache | Lösung |
|---|---|---|
| "Self-signed certificate in certificate chain" | Der Client vertraut der Root-CA nicht oder die Root-CA wurde nicht im Trust-Store installiert. | Prüfen, ob update-ca-certificates (Debian) oder update-ca-trust (Rocky) ausgeführt wurde. Browser ggf. neu starten. |
| "Depth lookup: unable to get local issuer certificate" | Die Intermediate-CA (Sub-CA) fehlt in der Kette, die der Server ausliefert. | Prüfen, ob die Datei fullchain.pem korrekt erstellt wurde (Reihenfolge!) und im Webserver (SSLCertificateFile / ssl_certificate) eingebunden ist. |
| "Certificate is not valid for 'xyz.int'" | Der Subject Alternative Name (SAN) fehlt oder ist falsch geschrieben. | Zertifikat prüfen mit: openssl x509 -in cert.crt -text -> Suche nach "X509v3 Subject Alternative Name". |
| "Verification error: certificate has expired" | Systemzeit auf Server oder Client ist falsch (häufig bei VMs nach Suspend). | Datum/Uhrzeit mit dem Befehl date prüfen und ggf. per NTP synchronisieren. |
| "Modulus mismatch" | Der private Schlüssel passt kryptografisch nicht zum vorliegenden Zertifikat. | Vergleichen der MD5-Hashes beider Dateien (siehe unten unter "Nützliche Befehle"). |
| "CA:FALSE" bei der Sub-CA | Die Sub-CA wurde ohne die Extension basicConstraints=CA:TRUE signiert. | Die Sub-CA ist nicht berechtigt, weitere Zertifikate zu signieren. Sub-CA mit den richtigen Extensions neu erstellen. |
Nützliche Prüfbefehle für die Administration
Modulus-Check (Passen Key und CRT zusammen?)
Um sicherzustellen, dass ein Zertifikat zu einem Private Key gehört, müssen die Modulus-Hashes identisch sein:
openssl x509 -noout -modulus -in www.it213.int.crt | openssl md5
openssl rsa -noout -modulus -in www.it213.int.key | openssl md5
TLS-Handshake live testen
Simuliert einen Verbindungsaufbau und zeigt die vom Server gesendete Zertifikatskette an:
openssl s_client -connect localhost:443 -showcerts
Zertifikatsinhalt schnell prüfen
openssl x509 -in fullchain.pem -text -noout
Python Webserver mit SSL/TLS
In dieser Anleitung wird ein einfacher HTTPS-Webserver mit Python erstellt, der statische Inhalte aus einem Verzeichnis ausliefert.
Als erstes müssen wir die fullchain.pem und den privaten Schlüssl zu Server bringen
- scp fullchain.pem www.it213.int.key www:
Auf dem Webserver
- ssh kit@www
Verzeichnis erstellen
Zuerst wird ein Arbeitsverzeichnis für die Webinhalte und das Server-Skript angelegt.
- mkdir html
Keys anpassen
Die Zertifikatsdateien müssen in das Verzeichnis kopiert und für das Skript passend benannt werden.
- cp fullchain.pem html/own.crt
- cp www.it213.int.key html/own.key
Konfiguration
Das Python-Skript konfiguriert den Server so, dass er auf Port 8443 lauscht und die SSL-Verschlüsselung nutzt.
- nano html/https.py
import http.server, ssl
server_address = ('0.0.0.0', 8443)
handler = http.server.SimpleHTTPRequestHandler
httpd = http.server.HTTPServer(server_address, handler)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile="own.crt", keyfile="own.key")
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
print("HTTPS läuft auf https://0.0.0.0:8443")
httpd.serve_forever()
Statische Seite erstellen
Damit der Server eine Seite anzeigt, wird eine index.html im Verzeichnis erstellt.
- nano html/index.html
<html> <head> <title>IT213 Testseite</title> </head> <body> <h1>Erfolg!</h1> <p>Der verschlüsselte Python-Webserver funktioniert und liefert diese Seite aus.</p> </body> </html>
Starten
Um den Server zu starten, wechselt man in das Verzeichnis und führt das Skript aus.
- cd html
- python3 https.py
Der Zugriff erfolgt im Browser über https://www.it213.int:8443.
Tests vom Client
- Verschlüsselter Verbindungsaufbau
- openssl s_client -host www.it213.int -port 8443
- Welche Zertifikate werden angeboten?
- nmap --script ssl-cert www.it213.int -p 8443
- Welche SSL/TLS Versionen werden angeboten
- nmap --script ssl-enum-ciphers www.it213.int -p 8443