JavaRush /Java-Blog /Random-DE /Von HTTP zu HTTPS
Viacheslav
Level 3

Von HTTP zu HTTPS

Veröffentlicht in der Gruppe Random-DE
Von HTTP zu HTTPS – 1
Inhalt:

Einführung

In der modernen Welt können Sie ohne Webanwendungen nicht leben. Und wir beginnen mit einem kleinen Experiment. Als Kind erinnere ich mich, wie an allen Ständen eine Zeitung mit dem Titel „Argumente und Fakten“ verkauft wurde. Ich erinnerte mich an sie, weil diese Zeitungen meiner persönlichen Wahrnehmung aus der Kindheit zufolge immer seltsam aussahen. Und ich entschied, ob wir auf ihre Website gehen sollten:
Von HTTP zu HTTPS - 2
Wenn wir die Google Chrome-Hilfe aufrufen, erfahren wir, dass diese Website keine sichere Verbindung verwendet und die Informationen, die Sie mit der Website austauschen, möglicherweise für Dritte zugänglich sind. Schauen wir uns noch einige andere Nachrichten an, zum Beispiel die Nachrichten aus St. Petersburg von Fontanka, einem elektronischen Medium:
Von HTTP zu HTTPS - 3
Wie Sie sehen, weist die Website von Fontanka aufgrund dieser Daten keine Sicherheitsprobleme auf. Es stellt sich heraus, dass Webressourcen sicher sein können oder auch nicht. Wir sehen auch, dass der Zugriff auf ungeschützte Ressourcen über das HTTP-Protokoll erfolgt. Und wenn die Ressource geschützt ist, erfolgt der Datenaustausch über das HTTPS-Protokoll, wobei das S am Ende „Sicher“ bedeutet. Das HTTPS-Protokoll ist in der Spezifikation rfc2818 beschrieben: „ HTTP Over TLS “. Versuchen wir, unsere eigene Webanwendung zu erstellen und selbst zu sehen, wie sie funktioniert. Und nebenbei werden wir die Begriffe verstehen.
Von HTTP zu HTTPS – 4

Webanwendung in Java

Wir müssen also eine sehr einfache Webanwendung in Java erstellen. Zunächst benötigen wir die Java-Anwendung selbst. Dazu verwenden wir das automatische Build-System des Gradle-Projekts. Dadurch müssen wir die nötige Verzeichnisstruktur nicht manuell erstellen + Gradle verwaltet für uns alle für das Projekt notwendigen Bibliotheken und stellt sicher, dass diese bei der Ausführung des Codes verfügbar sind. Mehr über Gradle können Sie in einer kurzen Rezension lesen: „ Eine kurze Einführung in Gradle “. Lassen Sie uns das Gradle Init Plugin verwenden und den Befehl ausführen:
gradle init --type java-application
Danach öffnen wir das Build-Skript build.gradle, das beschreibt, aus welchen Bibliotheken unser Projekt besteht und die uns Gradle zur Verfügung stellt. Fügen wir dort eine Abhängigkeit vom Webserver hinzu, auf dem wir experimentieren werden:
dependencies {
    // Web server
    implementation 'io.undertow:undertow-core:2.0.20.Final'
     // Use JUnit test framework
     testImplementation 'junit:junit:4.12'
}
Damit eine Webanwendung funktioniert, benötigen wir unbedingt einen Webserver, auf dem unsere Anwendung gehostet wird. Es gibt eine große Vielfalt an Webservern, die wichtigsten sind jedoch: Tomcat, Jetty, Undertow. Dieses Mal werden wir Undertow wählen. Um zu verstehen, wie wir mit unserem Webserver arbeiten können, gehen wir auf die offizielle Undertow- Website und gehen zum Dokumentationsbereich . Sie und ich haben eine Abhängigkeit von Undertow Core verbunden, daher interessiert uns der Abschnitt über genau diesen Core , also den Kern, die Basis des Webservers. Der einfachste Weg ist die Verwendung der Builder-API für Undertow:
public static void main(String[] args) {
	Undertow server = Undertow.builder()
            .addHttpListener(8080, "localhost")
            .setHandler(new HttpHandler() {
                @Override
                public void handleRequest(final HttpServerExchange exchange) throws Exception {
                    exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
                    exchange.getResponseSender().send("Hello World");
                }
            }).build();
    server.start();
}
Wenn wir den Code ausführen, können wir zur folgenden Webressource navigieren:
Von HTTP zu HTTPS – 5
Es funktioniert einfach. Dank der Undertow Builder API fügen wir einen HTTP-Listener zu localhost und Port 8080 hinzu. Dieser Listener empfängt Anfragen vom Webbrowser und gibt als Antwort die Zeichenfolge „Hello World“ zurück. Tolle Webanwendung. Aber wie wir sehen, verwenden wir das HTTP-Protokoll, d.h. Diese Art des Datenaustauschs ist nicht sicher. Lassen Sie uns herausfinden, wie der Austausch mithilfe des HTTPS-Protokolls erfolgt.
Von HTTP zu HTTPS - 6

Anforderungen für HTTPS

Um zu verstehen, wie man HTTPS aktiviert, kehren wir zur HTTPS-Spezifikation zurück: „ RFC-2818: HTTP Over TLS “. Laut Spezifikation werden Daten im HTTPS-Protokoll über die kryptografischen Protokolle SSL oder TLS übertragen. Menschen werden oft durch das Konzept von SSL und TLS in die Irre geführt. Tatsächlich hat sich SSL weiterentwickelt und seine Versionen geändert. Später wurde TLS der nächste Schritt in der Entwicklung des SSL-Protokolls. Das heißt, TLS ist einfach eine neue Version von SSL. In der Spezifikation heißt es: „SSL und sein Nachfolger TLS“. Wir haben also erfahren, dass es kryptografische SSL/TLS-Protokolle gibt. SSL ist eine Abkürzung für Secure Sockets Layer und bedeutet übersetzt „sichere Socket-Schicht“. Aus dem Englischen übersetzt ist Socket ein Stecker. Teilnehmer an der Datenübertragung über ein Netzwerk nutzen Sockets als Programmierschnittstelle (also eine API), um über das Netzwerk miteinander zu kommunizieren. Der Browser fungiert als Client und verwendet einen Client-Socket, und der Server, der eine Anfrage empfängt und eine Antwort ausgibt, verwendet einen Server-Socket. Und zwischen diesen Sockets findet der Datenaustausch statt. Deshalb hieß das Protokoll ursprünglich SSL. Aber die Zeit verging und das Protokoll entwickelte sich weiter. Und irgendwann wurde aus dem SSL-Protokoll das TLS-Protokoll. TLS ist die Abkürzung für Transport Layer Security. Das TLS-Protokoll wiederum basiert auf der SSL-Protokollspezifikation Version 3.0. Das TLS-Protokoll ist Gegenstand separater Artikel und Rezensionen, daher werde ich lediglich Materialien angeben, die ich interessant finde: Kurz gesagt, die Grundlage von HTTPS ist der TLS-Handshake und die „Server Identity“-Prüfung (d. h. Serveridentifikation) anhand seines digitalen Zertifikats. Es ist wichtig. Erinnern wir uns daran, denn... Wir werden später auf diese Tatsache zurückkommen. Daher haben wir zuvor HttpListener verwendet, um dem Server mitzuteilen, wie er über das HTTP-Protokoll arbeiten soll. Wenn wir im obigen Beispiel einen HttpListener hinzugefügt haben, um über HTTP zu arbeiten, müssen wir für die Arbeit über HTTPS einen HttpsListener hinzufügen:
Von HTTP zu HTTPS - 7
Aber um es hinzuzufügen, benötigen wir SSLContext. Interessanterweise ist SSLContext keine Klasse von Undertow, sondern javax.net.ssl.SSLContext. Die SSLContext-Klasse ist Teil der sogenannten „ Java Secure Socket Extension “ (JSSE) – einer Java-Erweiterung zur Gewährleistung der Sicherheit von Internetverbindungen. Diese Erweiterung wird im „ Java Secure Socket Extension (JSSE) Reference Guide “ beschrieben. Wie Sie dem einleitenden Teil der Dokumentation entnehmen können, stellt JSSE ein Framework und eine Java-Implementierung der SSL- und TLS-Protokolle bereit. Wie erhalten wir den SSLContext? Öffnen Sie den JavaDoc SSLContext und suchen Sie die getInstance- Methode . Wie Sie sehen, müssen wir zum Abrufen des SSLContext den Namen „Secure Socket Protocol“ angeben. Aus der Beschreibung der Parameter geht hervor, dass diese Namen in der „ Java Cryptography Architecture Standard Algorithm Name Documentation “ zu finden sind. Folgen wir daher den Anweisungen und gehen wir zur Dokumentation. Und wir sehen, dass wir zwischen SSL und TLS wählen können:
Von HTTP zu HTTPS - 8
Jetzt verstehen wir, dass wir den SSLContext wie folgt erstellen müssen:
public SSLContext getSSLContext() {
	// 1. Получаем контекст, в рамках которого будем работать по TLS протоколу
	SSLContext context = null;
	try {
		context = SSLContext.getInstance("TLS");
	} catch (NoSuchAlgorithmException e) {
		throw new IllegalStateException(e);
	}
	return context;
}
Nachdem wir einen neuen Kontext erstellt haben, erinnern wir uns daran, dass SSLContext im „ Java Secure Socket Extension (JSSE) Reference Guide “ beschrieben wurde. Wir lesen und sehen, dass „ein neu erstellter SSLContext durch Aufruf der Init-Methode initialisiert werden sollte“. Das heißt, es reicht nicht aus, einen Kontext zu schaffen. Es muss initialisiert werden. Und das ist logisch, denn Bezüglich der Sicherheit haben wir Ihnen lediglich mitgeteilt, dass wir das TLS-Protokoll verwenden möchten. Um den SSLContext zu initialisieren, müssen wir drei Dinge bereitstellen: KeyManager, TrustManager, SecureRandom.
Von HTTP zu HTTPS - 9

Schlüsselmanager

KeyManager ist ein Schlüsselmanager. Er ist dafür verantwortlich, welche „Authentifizierungsdaten“ jemandem zur Verfügung gestellt werden, der uns kontaktiert. Credential kann als Identität übersetzt werden. Die Identität wird benötigt, damit der Client sicher sein kann, dass der Server der ist, für den er sich ausgibt, und dass ihm vertraut werden kann. Was wird als Identifikation verwendet? Wie wir uns erinnern, wird die Serveridentität durch das digitale Zertifikat des Servers überprüft. Dieser Vorgang lässt sich wie folgt darstellen:
Von HTTP zu HTTPS – 10
Darüber hinaus heißt es im „ JSSE Reference Guide: How SSL Works “, dass SSL „asymmetrische Kryptographie“ verwendet, was bedeutet, dass wir ein Schlüsselpaar benötigen: einen öffentlichen Schlüssel und einen privaten Schlüssel. Da es sich um Kryptographie handelt, kommt die „Java Cryptography Architecture“ (JCA) ins Spiel. Oracle stellt ein hervorragendes Dokument zu dieser Architektur bereit: „ Java Cryptography Architecture (JCA) Reference Guide “. Darüber hinaus können Sie auf JavaRush einen kurzen Überblick über JCA lesen: „ Java Cryptography Architecture: Erste Bekanntschaft “. Um den KeyManager zu initialisieren, benötigen wir also einen KeyStore, der das Zertifikat unseres Servers speichert. Die gebräuchlichste Methode zum Erstellen eines Schlüssel- und Zertifikatspeichers ist das Dienstprogramm keytool, das im JDK enthalten ist. Ein Beispiel finden Sie in der JSSE-Dokumentation: „ Creating a Keystore to Use with JSSE “. Daher müssen wir das Dienstprogramm KeyTool verwenden, um einen Schlüsselspeicher zu erstellen und das Zertifikat dort zu schreiben. Interessanterweise wurde die Schlüsselgenerierung zuvor mit -genkey angegeben, jetzt wird jedoch die Verwendung von -genkeypair empfohlen. Wir müssen die folgenden Dinge definieren:
  • alias : Alias ​​oder einfach der Name, unter dem der Eintrag im Keystore gespeichert wird
  • keyalg : Schlüsselverschlüsselungsalgorithmus. Wählen wir den RSA-Algorithmus, der für unseren Zweck im Wesentlichen eine Standardlösung ist.
  • keysize : Schlüsselgröße (in Bits). Die empfohlene Mindestgröße ist 2048, weil... eine kleinere Größe wurde bereits geknackt. Mehr können Sie hier lesen: „ ein SSL-Zertifikat in 2048 Bit “.
  • dname : Distinguished Name, Distinguished Name.
Es ist wichtig zu verstehen, dass die angeforderte Ressource (z. B. https://localhost) damit verglichen wird. Dies wird als „Subjekt-CN-Matching“ bezeichnet.
  • Gültigkeit : Dauer in Tagen, während der das generierte Zertifikat gültig ist, d. h. gültig.
  • ext : In „ Benannte Erweiterungen “ angegebene Zertifikatserweiterung .
Für selbstsignierte Zertifikate (d. h. für unabhängig erstellte Zertifikate) müssen Sie die folgenden Erweiterungen angeben:
  • -ext san:critical=dns:localhost,ip:127.0.0.1 > um den Betreffabgleich nach SubjectAlternativeName durchzuführen
  • -ext bc=ca:false > um anzugeben, dass dieses Zertifikat nicht zum Signieren anderer Zertifikate verwendet wird
Lassen Sie uns den Befehl ausführen (Beispiel für Windows-Betriebssystem):
keytool -genkeypair -alias ssl -keyalg RSA -keysize 2048 -dname "CN=localhost,OU=IT,O=Javarush,L=SaintPetersburg,C=RU,email=contact@email.com" -validity 90 -keystore C:/keystore.jks -storepass passw0rd -keypass passw0rd -ext san:critical=dns:localhost,ip:127.0.0.1 -ext bc=ca:false
Weil Damit die Datei erstellt wird, stellen Sie sicher, dass Sie über alle Rechte zum Erstellen der Datei verfügen. Sie werden wahrscheinlich auch Ratschläge wie diesen sehen:
Von HTTP zu HTTPS - 11
Hier wird uns gesagt, dass JKS ein proprietäres Format ist. Proprietär bedeutet, dass es sich um privates Eigentum der Autoren handelt und nur für die Verwendung in Java vorgesehen ist. Bei der Arbeit mit Dienstprogrammen von Drittanbietern kann es zu Konflikten kommen, weshalb wir gewarnt werden. Darüber hinaus erhalten wir möglicherweise die Fehlermeldung: The destination pkcs12 keystore has different storepass and keypass. Dieser Fehler tritt auf, weil die Passwörter für den Eintrag im Keystore und für den Keystore selbst unterschiedlich sind. In der Keytool- Dokumentation heißt es : „Zum Beispiel erfordern die meisten Tools von Drittanbietern, dass Storepass und Keypass in einem PKCS #12-Keystore identisch sind.“ Den Schlüssel können wir selbst angeben (z. B. -destkeypass enterpassw). Es ist jedoch besser, die Anforderungen nicht zu verletzen und das gleiche Passwort festzulegen. Der Import könnte also so aussehen:
keytool -importkeystore -srckeystore C:/keystore.jks -destkeystore C:/keystore.jks -deststoretype pkcs12
Erfolgsbeispiel:
Von HTTP zu HTTPS - 12
Um das Zertifikat in eine Datei zu exportieren, können Sie Folgendes ausführen:
keytool -export -alias ssl -storepass passw0rd -file C:/server.cer -keystore C:/keystore.jks
Darüber hinaus können wir den Keystore-Inhalt wie folgt abrufen:
keytool -list -v -keystore C:/keystore.jks -storepass passw0rd
Großartig, jetzt haben wir einen Keystore, der das Zertifikat enthält. Jetzt können Sie es aus dem Code abrufen:
public KeyStore getKeyStore() {
	// Согласно https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore
	try(FileInputStream fis = new FileInputStream("C:/keystore.jks")){
		KeyStore keyStore = KeyStore.getInstance("pkcs12");
		keyStore.load(fis, "passw0rd".toCharArray());
		return keyStore;
	} catch (IOException ioe) {
		throw new IllegalStateException(ioe);
	} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException e) {
		throw new IllegalStateException(e);
	}
}
Wenn ein KeyStore vorhanden ist, können wir den KeyManager initialisieren:
public KeyManager[] getKeyManagers(KeyStore keyStore) {
	String keyManagerAlgo = KeyManagerFactory.getDefaultAlgorithm();
	KeyManagerFactory keyManagerFactory = null;
	try {
		keyManagerFactory = KeyManagerFactory.getInstance(keyManagerAlgo);
		keyManagerFactory.init(keyStore, "passw0rd".toCharArray());
		return keyManagerFactory.getKeyManagers();
	} catch (NoSuchAlgorithmException e) {
		throw new IllegalStateException(e);
	} catch (UnrecoverableKeyException | KeyStoreException e) {
		throw new IllegalStateException(e);
	}
}
Unser erstes Ziel ist erreicht. Es bleibt herauszufinden, was TrustManager ist. TrustManager wird in der JSSE-Dokumentation im Abschnitt „ Die TrustManager-Schnittstelle “ beschrieben. Es ist KeyManager sehr ähnlich, dient jedoch dazu, zu überprüfen, ob der Person, die die Verbindung anfordert, vertraut werden kann. Um es ganz klar auszudrücken: Dies ist der umgekehrte KeyManager =) Wir brauchen TrustManager nicht, also übergeben wir null. Anschließend wird ein Standard-TrustManager erstellt, der den Endbenutzer, der Anfragen an unseren Server stellt, nicht überprüft. In der Dokumentation heißt es: „Es wird die Standardimplementierung verwendet“. Das Gleiche gilt für SecureRandom. Wenn wir null angeben, wird die Standardimplementierung verwendet. Denken wir daran, dass SecureRandom eine JCA-Klasse ist und in der JCA-Dokumentation im Abschnitt „ Die SecureRandom-Klasse “ beschrieben wird. Insgesamt könnte die Vorbereitung unter Berücksichtigung aller oben beschriebenen Methoden wie folgt aussehen:
public static void main(String[] args) {
	// 1. Подготавливаем приложение к работе по HTTPS
	App app = new App();
	SSLContext sslContext = app.getSSLContext();
	KeyStore keyStore = app.getKeyStore();
	KeyManager[] keyManagers = app.getKeyManagers(keyStore);
	try {
		sslContext.init(keyManagers, null, null);
	} catch (KeyManagementException e) {
		throw new IllegalStateException(e);
	}
Jetzt muss nur noch der Server gestartet werden:
// 2. Поднимаем Server
 	int httpsPort = 443;
	Undertow server = Undertow.builder()
            .addHttpsListener(httpsPort, "localhost", sslContext)
            .setHandler(new HttpHandler() {
                @Override
                public void handleRequest(final HttpServerExchange exchange) throws Exception {
                    exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
                    exchange.getResponseSender().send("Hello World");
                }
            }).build();
	server.start();
}
Dieses Mal wird unser Server unter der folgenden Adresse erreichbar sein. https://localhost:443 Wir erhalten jedoch weiterhin eine Fehlermeldung, dass diese Ressource nicht vertrauenswürdig ist:
Von HTTP zu HTTPS - 13
Lassen Sie uns herausfinden, was mit dem Zertifikat nicht stimmt und was wir dagegen tun können.
Von HTTP zu HTTPS – 14

Zertifikatsverwaltung

Unser Server ist also bereits bereit, über HTTPS zu arbeiten, aber der Client vertraut ihm nicht. Warum? Werfen wir einen Blick darauf:
Von HTTP zu HTTPS – 15
Der Grund dafür ist, dass es sich bei diesem Zertifikat um ein selbstsigniertes Zertifikat handelt. Ein selbstsigniertes SSL-Zertifikat bezieht sich auf ein Public-Key-Zertifikat, das von derselben Person ausgestellt und signiert wurde, die es identifiziert. Das heißt, es wurde nicht von einer angesehenen Zertifizierungsstelle (CA, auch Certificate Authority genannt) ausgestellt. Die Zertifizierungsstelle fungiert als Treuhänder und ähnelt im Alltag einem Notar. Er versichert, dass die von ihm ausgestellten Zertifikate zuverlässig sind. Der Service der Ausstellung von Zertifikaten durch solche Zertifizierungsstellen ist kostenpflichtig, sodass niemand Vertrauensverluste und Reputationsrisiken ausgesetzt ist. Standardmäßig gibt es mehrere vertrauenswürdige Zertifizierungsstellen. Diese Liste kann bearbeitet werden. Und jedes Betriebssystem verfügt über eine eigene Verwaltung der Liste der Zertifizierungsstellen. Die Verwaltung dieser Liste in Windows können Sie beispielsweise hier nachlesen: „ Vertrauenswürdige Stammzertifikate in Windows verwalten “. Fügen wir das Zertifikat zu den vertrauenswürdigen hinzu, wie in der Fehlermeldung angegeben. Laden Sie dazu zunächst das Zertifikat herunter:
Von HTTP zu HTTPS – 16
Drücken Sie im Betriebssystem Windows Win+R und führen Sie die Tastenkombination aus mmc, um die Steuerkonsole aufzurufen. Drücken Sie anschließend Strg+M, um den Abschnitt „Zertifikate“ zur aktuellen Konsole hinzuzufügen. Als nächstes führen wir im Unterabschnitt „Vertrauenswürdige Stammzertifizierungsstellen“ aus Действия / Все задачи / Импорт. Importieren wir die zuvor heruntergeladene Datei in die Datei. Der Browser hat sich möglicherweise an den letzten Vertrauensstatus des Zertifikats erinnert. Daher müssen Sie vor dem Öffnen der Seite den Browser neu starten. Beispielsweise müssen Sie in Google Chrome in der Adressleiste Folgendes ausführen chrome://restart. Unter Windows können Sie das Dienstprogramm auch zum Anzeigen von Zertifikaten verwenden certmgr.msc:
Von HTTP zu HTTPS – 17
Wenn wir alles richtig gemacht haben, sehen wir einen erfolgreichen Aufruf unseres Servers über HTTPS:
Von HTTP zu HTTPS – 18
Wie Sie sehen, gilt das Zertifikat nun als gültig, die Ressource ist verfügbar und es liegen keine Fehler vor.
Von HTTP zu HTTPS – 19

Endeffekt

Wir haben also herausgefunden, wie das Schema zum Aktivieren des HTTPS-Protokolls auf einem Webserver aussieht und was dafür erforderlich ist. Ich hoffe, an dieser Stelle ist klar, dass die Unterstützung durch das Zusammenspiel der Java Cryptography Architecture (JCA), die für die Kryptographie verantwortlich ist, und der Java Secure Socket Extension (JSSE), die die TLS-Implementierung auf der Java-Seite bereitstellt, erfolgt. Wir haben gesehen, wie das im JDK enthaltene Dienstprogramm keytool für die Arbeit mit dem Schlüssel- und Zertifikatsspeicher KeyStore verwendet wird. Darüber hinaus haben wir festgestellt, dass HTTPS aus Sicherheitsgründen SSL/TLS-Protokolle verwendet. Um dies zu untermauern, empfehle ich Ihnen, hervorragende Artikel zu diesem Thema zu lesen: Hoffentlich wird HTTPS nach diesem kleinen Rückblick etwas transparenter. Und wenn Sie HTTPS aktivieren müssen, können Sie die Begriffe leicht aus der Dokumentation Ihrer Anwendungsserver und Frameworks nachvollziehen. #Wjatscheslaw
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION