SSL, TLS, Zertifikate und Private-Key Verschlüsselung

Dies sind die größten Geheimnisse der Menschheit:

  1. Quantenverschränkung
  2. Dunkle Materie
  3. SSL-Zertifikate

Wenigstens das letzte konnte ich jetzt aufdröseln und enträtseln (beim ersten bin ich dran). Vielleicht geht es dir wie mir: Immer wenn man selbst-signierte Zertifikate braucht, liegen vorgefertigte Konsolenzeilen bereit. Die funktionieren einfach. Bitte nicht hinterfragen.

Aber jetzt nicht mehr! Wir hinterfragen alles. Wir wollen verstehen, was SSL, TLS, Zertifikate, CA’s, OpenSSL und das ganze Zeugs bedeuten! Ein für alle mal!

Oder bis nächste Woche, wenn wir wieder alles vergessen haben.

Grundsätzliches zu Zertifikaten

Bevor wir uns anschauen, wie wir Zertifikate generieren, muss erstmal klar sein, was das überhaupt ist. Also: Zertifikate ermöglichen eine gesicherte Verbindung zwischen zwei Rechnern (z.B. Browser und Server) über Private-/Public-Key-Verschlüsselung. Als Goodie obendrauf wird sichergestellt, dass beide Parteien auch die sind, für die sie sich ausgeben (die sogenannte Signierung).

Infolgedessen besteht ein vollständiges Zertifikat aus einem Private Key (geheim), einem Public Key (öffentlich) und einem Teil, der von einer dritten Partei, nämlich einer vertrauenswürdigen Zertifizierungsstelle (oder auch CA – Certificate Authority), signiert wurde; damit der andere weiß, dass dessen Zertifikat echt ist.

Nur zur Vollständigkeit, falls es dir nicht bekannt ist: Daten können immer mit jeweils einem Key ver- und mit dem anderen entschlüsselt werden. Den Public-Key gibt man frei heraus (daher der Name), der Private-Key bleibt im geheimen Besitz. Das Gegenüber verschlüsselt seine Nachricht mit dem Public-Key, der Empfänger nutzt zur Entschlüsselung seinen Private-Key. So einfach ist das.

Damit wir den zusätzlichen Schutz der Authentifizierung haben, muss eine Zertifizierungsstelle dem Keypair ihren Stempel aufsetzen (“Signing”). Deshalb muss man dieser zunächst ein paar Grundinformationen mitteilen. Dies sind z.B. Firmenname, Email und auch Domain. Gespeichert wird das in einer CSR-Datei (Certificate Signing Request). Auch die Zertifizierungsstelle hat ein Zertifikat, dem alle glauben!

Um ein eigenes signiertes Zertifikat zu erhalten, sendet man die CSR-Datei (die übrigens auch den Public-Key enthält) an die Zertifizierungsstelle. Je nach Aufwandsgrad wird nun die Firma oder die Domain geprüft. Anschließend sendet der CA ein signiertes Zertifikat zurück, das man benutzen kann.

Soweit erstmal die Grundlagen – fangen wir an!

Halt Stop! Dateiendungen…

Ein bisschen komisch, jetzt über Dateiendungen zu sprechen, aber leider ist es so, dass hier ein bisschen Chaos herrscht. Damit uns das nicht im Weg steht, einigen wir uns auf die (hoffentlich) am weitesten verbreitete Schreibweise:

  • .key: Private/Public-Keypair (geheim halten!)
  • .csr: CSR-Anfrage (enthält alle öffentlichen Infos zum Keypair)
  • .crt: Das endgültige und signierte Zertifikat
  • .pem: Andere Schlüsselteile, bzw. Kombinationen; beispielsweise nur der Public Key oder signiertes Zertifikat mit Private Key. Es kann vorkommen, dass auch Zertifikate mit dieser Dateiendung herumspazieren. Aber nicht bei uns!

Das ultimative Tool: openssl

Mit openssl, das bereits auf Ubuntu-Systemen vorhanden ist, können wir alles machen: Keys und Zertifikate erstellen, Signieren, Umwandeln, Verschlüsseln, etc.

Dazu beginnt jeder Aufruf mit openssl, gefolgt von dem Modul, das wir nutzen wollen. Vorweggenommen ist das:

  • genpkey: Keys generieren
  • rsa: Informationen aus Keys ziehen
  • enc: Verschlüsseln
  • req: CSR-Datei erstellen
  • x509: Zertifikat erstellen/signieren
  • verify: Zertifikate prüfen

Der Private Key ist der Schlüssel

Ohne einen Private Key geht gar nichts. Das ist eine Zeichenfolge, die praktisch ein langes Passwort ist und niemals in die Hände von fremden Personen gelangen sollte. So einen Private Key erstellt man ganz einfach über openssl genrsa (erstelle vorher vielleicht dafür ein eigenes Verzeichnis):

# Ohne Passwort
openssl genpkey -algorithm RSA -out beispiel.key

# Mit Passwort
openssl genpkey -algorithm RSA -out beispiel.key -aes256

Das erzeugt eine Datei, bzw. einen 2048-bit-Schlüssel, der ungefähr so aussieht:

Solch einen Key musst du später für jeden Server oder Client generieren, der verschlüsselt kommunizieren möchte. Das bedeutet ein Keyfile für jede MySQL-Instanz, oder auch jeden Browser (der das selbst macht).

Den Key solltest du übrigens nicht verschlüsseln, wenn dieser z.B. von Apache genutzt wird. Ansonsten müsstest du das Passwort bei jedem Neustart eingeben. Das macht aber durchaus manchmal Sinn, wirst schon sehen!

Wo ist der Public Key?

Tatsächlich generiert man mit obigem Befehl nicht nur den Private Key, sondern auch sein öffentliches Pendant. Mit openssl rsa kann man diesen in eine Datei schreiben:.

openssl rsa -in beispiel.key -pubout -out beispiel-public.pem

Der Key sieht dann ungefähr so aus:

Kleiner Exkurs: Dateien verschlüsseln

Jetzt hast du also ein RSA-Keypair erstellt. Was man damit macht? Hauptsächlich verschlüsseln, also machen wir das doch einmal mit einer Datei.

Haaaaaaaaaaalt Stopp. Leider kann man mit einem RSA-Key nur Texte bis zu einer Länge des Keys (also in unserem Fall 2048 Bytes) verschlüsseln. Hä? Für Grundschulaufsätze mag das vielleicht reichen, aber sonst nicht…

Der Trick ist, dass Dateien zunächst “synchron” per AES verschlüsselt werden – und zwar mit einem langen Zufallspasswort. Synchron bedeutet, dass man die Datei mit dem gleichen Passwort wieder entschlüsseln kann – im Gegensatz zu RSA, wo man jeweils für einen Teil den Private, bzw. Public Key benötigt.

Damit der Exkurs nicht zu lang wird, machen wir das jetzt in einem Rutsch: Testdatei erstellen, langes Passwort generieren, Testdatei damit verschlüsseln und das lange Passwort mit unserem Public Key.

# Testdatei erstellen
echo 'Hallo, verschlüsselte Welt!' > verschluesselung-test.txt

# Langes Passwort erstellen
openssl rand -out verschluesselung-passwort.txt -hex 64 

# Datei mit AES verschlüsseln
openssl enc -aes-256-cbc -salt -pbkdf2 -in verschluesselung-test.txt -out verschluesselung-test.txt.enc -pass file:verschluesselung-passwort.txt

# Passwort-Datei mit RSA (Public Key) verschlüsseln
openssl rsautl -encrypt -inkey beispiel-public.pem -pubin -in verschluesselung-passwort.txt -out verschluesselung-passwort.txt.enc

# Beide *.enc Dateien könnten jetzt verschickt werden!

# Verschlüsselte Passwort-Datei mit RSA (Private Key) entschlüsseln
openssl rsautl -decrypt -inkey beispiel.key -in verschluesselung-passwort.txt.enc -out verschluesselung-passwort-entschluesselt.txt

# Verschlüsselte Datei mit AES entschlüsseln
openssl enc -d -aes-256-cbc -pbkdf2 -in verschluesselung-test.txt.enc -out verschluesselung-test-entschluesselt.txt -pass file:verschluesselung-passwort-entschluesselt.txt

CSR erstellen

Zu jedem Keypair, das wir ja jetzt erzeugt haben (beispiel.key), gehört für ein Zertifikat ein Certificate Signing Request. Den erstellt man mit openssl req.

openssl req -new -key beispiel.key -out beispiel.csr

Die Infos, die du hier einträgst, können für die Authentifizierung sehr wichtig sein, z.B. Common Name. Achte darauf, dass du hier die IP oder die Domain des Servers einträgst, da viele Systeme wie MySQL sonst nicht mit dem Zertifikat funktionieren.

Diese CSR-Datei kannst du nun an eine Zertifizierungsstelle schicken, um ein “öffentliches” Zertifikat zu erhalten, das überall akzeptiert wird. Es ist aber auch möglich, ein Zertifikat so zu signieren, dass du selbst der Zertifizierer bist. Dazu später mehr.

Alles in einem: Keypair und CSR erstellen

Natürlich gibt es einen Befehl, um das obige in einem Rutsch (ohne Public-Key-Export) zu erledigen:

openssl req -newkey rsa:2048 -nodes -keyout beispiel2.key -out beispiel2.csr

Zertifikate selbst signieren

Oft musst du Zertifikate gar nicht fremd authentifizieren lassen. Für die interne Kommunikation zwischen Servern reicht es aus, selbst-signierte Dateien zu benutzen.

Eine Signierung aus den gerade erstellten Dateien erhälst du mit openssl x509:

openssl x509 -in beispiel2.csr -out beispiel2.crt -req -signkey beispiel2.key -days 3650

Zur Zusammenfassung der Dateien:

  • beispiel2.key: Das RSA Private/Public-Keypair
  • beispiel2.csr: Die Anfrage für die Zertifikatserstellung, beinhaltet auch z.B. IP oder Domain
  • beispiel2.crt: Das signierte Zertifikat

Je nach System werden diese anders eingesetzt. Bei MariaDB gibst du z.B. Private-Key und Zertifikat gesondert an, bei MongoDB packst du Private-Key und Zertifikat in eine Datei. Das geht sehr gut, da alle Dateien in Blöcke gegliedert sind; auch dazu später mehr.

Nochmal in einem Rutsch: Keypair, (CSR), Self-Signing

Das alles geht auch in einer Zeile:

openssl req -new -days 3650 -newkey rsa:2048bits -x509 -nodes -out beispiel3.crt -keyout beispiel3.key

Eine CSR-Datei wird dabei nicht erstellt.

Hinweis: Der Parameter -nodes bedeutet eigentlich “No DES” und sorgt dafür, dass das Keyfile nicht verschlüsselt wird.

Die eigene Certificate Authority sein

Selbst signierte Zertifikate sind OK – damit kann man schnell eine Verbindung zwischen zwei Servern verschlüsseln. Nachteil: Es gibt keine Überprüfung, ob man dem Zertifikat, bzw. dem Gegenüber, wirklich vertrauen kann. Das kann wichtig sein, wenn man eine Verschlüsselung mit einem fremden Server einer Firma nutzen möchte. Dann sollte man prüfen, ob das Zertifikat wirklich von dieser Firma ausgestellt wurde.

Das erreicht man damit, indem man seine eigene Certificate Authority wird. Alle bekannten Firmen wie IdenTrust oder DigiTrust haben ein “Root-Zertifikat”, das jeder Browser gespeichert hat. Man muss also nichts anderes tun, als ein eigenes Root-Zertifkat zu erstellen und dieses dahin zu kopieren, wo die anderen liegen.

Praxisbeispiel-Time! Wenn wir uns das TLS-Zertifikat von Google ansehen, finden wir folgendes heraus:

Google Trust Services ist (Überraschung) die Zertifizierungsstelle von Google. In Windows findet man die Root-Zertifikate unter “Computerzertifikate verwalten”.

Unter Ubuntu findet man diese in /usr/share/ca-certificates.

Damit wir also unserem eigenen Zertifikat vertrauen können, müssen wir nur selbst ein Root-Zertifikat erstellen und es in dieses Verzeichnis packen!

CA-Zertifikat erstellen

Ein CA-Root-Zertifikat unterscheidet sich nicht von einem anderen Zertifikat, das selbst signiert ist. Theoretisch könnten wir dieses auch von einer Zertifizierungsstelle authorisieren lassen – aber das ist nicht nötig, wenn wir uns selbst glauben.

Wir erstellen es mit nur einer Zeile (ohne -nodes, da wir ein Passwort wollen, so dass niemand nur mit der Datei Zertifikate erstellen kann). Du erhälst dann eine CRT- und eine KEY-Datei.

openssl req -new -extensions v3_ca -days 3650 -newkey rsa:2048bits -x509 -out mein-ca.crt -keyout mein-ca.key

Hinweis: Der Parameter -extensions v3_ca erstellt das Zertifikat mit Standard-Optionen für Root-CA-Zertifikate, die in der OpenSSL-Konfiguration stehen.

Achtung: Als Common-Name kannst du einen beliebigen Namen verwenden. Das CA-Zertifikat ist praktisch “domainlos”. Außerdem musst du ein Passwort für die CA-Datei festlegen (“PEM pass phrase”).

Das war es schon! Kopiere die CRT-Datei einfach nach /usr/local/share/ca-certificates – wir werden später prüfen, ob es geklappt hat. Anschließend musst du die Zertifikate neu laden.

sudo cp mein-ca.crt /usr/local/share/ca-certificates
sudo update-ca-certificates

CA-Zertifikat überprüfen

Einfach zum Spaß lassen wir uns die Infos unseres Zertifikats mal anzeigen. Das kannst du mit jedem machen, um z.B. herauszufinden, was der Common Name ist. Es gibt auch Online-Dienste, die sowas können!

openssl x509 -in mein-ca.crt -text

CA-Zertifikat in Windows importieren

Dein mein-ca.crt CA-Zertifikat kannst du ganz einfach in Windows importieren, indem du auf die Datei doppelklickst.

Manchmal öffnet sich dann ein Fenster (wenn du Glück hast):

Hier klickst du auf “Zertifikat installieren” und wählst den Speicher “Vertrauenswürdige Stammzertifizierungstellen” aus.

CA-Zertifikat in Firefox

Google übernimmt die Zertifikatsstellen von Windows, bei Firefox musst du in den Einstellungen das CA-Zertifikat selbst nochmal importieren:

Signieren mit eigenem CA-Zertifikat

Fehlt nur noch eins: Unser erstes CA-signiertes Zertifikat. Meines aktuellen Wissens geht das leider nicht in einem Befehl. Zusätzlich kommt noch etwas hinzu, denn Chrome (wie auch andere Browser) benötigen neben dem Common Name auch den subjectAltName. Was das ist? Jo.

Führe einfach die folgenden Schritte nacheinander aus.

Achtung: Du musst natürlich das Passwort eingeben, mit dem der CA-Key verschlüsselt ist.

# Die Variable $NAME mit dem Dateinamen füllen
# Die Variable $DOMAIN mit der Domain (inkl. Wildcard)
NAME=meinedomain.local 
DOMAIN=*.meinedomain.local

# Schlüsselpaar generieren, Common-Name ist schon mit vorhanden
openssl req -newkey rsa:2048 -nodes -keyout $NAME.key -out $NAME.csr -subj "/CN=$DOMAIN"

# Eine .ext Datei erstellen, die den subjectAltName beinhaltet
>$NAME.ext cat <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = $DOMAIN 
EOF

# Das Zertifikat mit unserem CA-Zertifikat signieren
openssl x509 -req -in $NAME.csr -CA mein-ca.crt -CAkey mein-ca.key -CAcreateserial -out $NAME.crt -days 825 -sha256 -extfile $NAME.ext

Fertig!

Hinweis: Den letzten Befehl führst du mit dem Parameter CAcreateserial aus. Dabei wird eine .srl Datei erstellt, die sich merkt, wieviele Zertifikate es gibt. Beim nächsten Zertifikat lässt du den Parameter einfach weg.

Zertifikate testen

Als nächstes wollen wir rausfinden, ob unser Zertifikat überhaupt valide ist. Das machen wir mit openssl verify. Nehmen wir mal beispiel3.crt als Beispiel – das haben wir selbst signiert, ohne unseren CA.

openssl verify beispiel3.crt

Heraus kommt folgendes:

Verification failed! Logisch, weil es selbst signiert ist, können wir dem nicht trauen.

Doch was ist mit unserem CA-signierten? Hier geben wir zuerst als Parameter das CA-File an, um sicher zu gehen:

openssl verify -CAfile mein-ca.crt beispiel4.crt

Das Ergebnis sollte beispiel4.crt: OK sein! Wenn du den CAFile-Parameter weglässt, solltest du das gleiche Resultat erhalten, da wir das Zertifikat vorhin an die richtige Stelle kopiert haben.

Sonstige Formate

Alle Keys und Zertifikate, die wir hier erstellt haben, sind im PEM-Format. Eine PEM-Datei kann mehrere Blöcke beinhalten, die alle per Base64 kodiert sind und auch benannt sind. Wenn du Private-Key und Zertifikat zusammenfassen musst (z.B. bei MongoDB), benutze einfach folgenden Befehl:

cat beispiel4.key beispiel4.crt > beispiel4-key-crt.pem

Oft begegnet dir auch das P12-Format (heißt auch PFX oder PKCS#12). Der Inhalt ist derselbe, nur ist die Datei immer mit einem Passwort geschützt. Man kann Zertifikate umwandeln mit openssl pkcs12. Dieses Format wird z.B. von Windows genutzt.

openssl pkcs12 -export -in beispiel4.crt -inkey beispiel4.key -out beispiel4.pfx -certfile mein-ca.crt

Beispiel: Let’s Encrypt/Certbot

Schauen wir uns doch mal ein paar Dienste an, die Zertifikate benutzen, bzw. erstellen. Der bekannteste dürfte Let’s Encrypt sein.

Der Dienst Certbot speichert alle Zertifikate unter /etc/letsencrypt/live/<Domain> (bzw. dort sind Symlinks).

  • privkey.pem: Private Key (unverschlüsselt)
  • cert.pem: Signiertes Zertifikat
  • chain.pem: CA-Zertifikat von Let’s Encrypt
  • fullchain.pem: Inhalte cert.pem und chain.pem zusammengefügt

Certbot überprüft die Domain über verschiedene Wege – entweder einen offenen HTTP-Port oder DNS-Einträge.

Beispiel: MariaDB/MySQL

MySQL hätte die SSL-Infos gerne als CA-, Zertifikat- und Key-File.

Beispiel: MongoDB

MongoDB präferiert ein CA-File und die Kombination aus Key und Zertifikat.

Beispiel: Putty

RSA-Keys werden auch für die SSH-Kommunikation genutzt. Dazu kennst du bestimmt das Tool puTTYgen.

Wenn du hier ein Keypair erstellst, wird es erstmal so angezeigt:

Der obere Teil ist die Public-Key Repräsentation für eine SSH-Verbindung (gibt man z.B. auch bei GitHub an).

Speichert man den Private-Key, erhält man ein Format ähnlich zu PEM – ist aber nur für Putty gedacht.

Diese Keypairs erstellst du allerdings entweder mit PuTTYgen oder in der Konsole mit ssh-keygen. Obwohl sie Ähnlichkeiten aufweisen, sind es doch zwei verschiedene Formate, die man sicherlich konvertieren kann – aber da es eigene Tools dafür gibt, macht das wenig Sinn.

Quellen

Björn Falszewski
21. Dezember 2020
Disclaimer
Alle meine Artikel entstehen mit bestem Wissen und Gewissen, sind aber nicht perfekt und sollten immer nur als Ausgangspunkt für deine eigenen Recherchen bilden.

Sollte dir etwas Fehlerhaftes auffallen, freue ich mich über deine Nachricht!