JavaRush /Blog Java /Random-FR /Du HTTP au HTTPS
Viacheslav
Niveau 3

Du HTTP au HTTPS

Publié dans le groupe Random-FR
Du HTTP au HTTPS - 1
Contenu:

Introduction

Dans le monde moderne, vous ne pouvez pas vivre sans applications Web. Et nous allons commencer par une petite expérience. Quand j'étais enfant, je me souviens comment tous les stands vendaient un journal tel que « Arguments et faits ». Je m'en souvenais parce que, selon ma perception personnelle de l'enfance, ces journaux avaient toujours l'air étranges. Et j'ai décidé si nous devions aller sur leur site Web :
Du HTTP au HTTPS - 2
Si nous allons dans l'aide de Google Chrome, nous lirons que ce site n'utilise pas de connexion sécurisée et que les informations que vous échangez avec le site peuvent être accessibles à des tiers. Voyons d'autres nouvelles, par exemple les nouvelles de Saint-Pétersbourg de Fontanka, un média électronique :
Du HTTP au HTTPS - 3
Comme vous pouvez le constater, le site Fontanka ne présente aucun problème de sécurité selon ces données. Il s’avère que les ressources Web peuvent être sûres ou non. On voit également que l'accès aux ressources non protégées se fait via le protocole HTTP. Et si la ressource est protégée, alors l'échange de données s'effectue via le protocole HTTPS, où le S à la fin signifie « Sécurisé ». Le protocole HTTPS est décrit dans la spécification rfc2818 : « HTTP Over TLS ». Essayons de créer notre propre application Web et voyons par nous-mêmes comment elle fonctionne. Et en chemin, nous comprendrons les termes.
Du HTTP au HTTPS - 4

Application Web en Java

Nous devons donc créer une application Web très simple en Java. Tout d’abord, nous avons besoin de l’application Java elle-même. Pour ce faire, nous utiliserons le système de build automatique du projet Gradle. Cela nous permettra de ne pas créer manuellement la structure de répertoires nécessaire + Gradle gérera pour nous toutes les bibliothèques nécessaires au projet et s'assurera qu'elles sont disponibles lors de l'exécution du code. Vous pouvez en savoir plus sur Gradle dans une courte revue : " Une brève introduction à Gradle ". Utilisons Gradle Init Plugin et exécutons la commande :
gradle init --type java-application
Après cela, ouvrons le script de construction build.gradle, qui décrit de quelles bibliothèques se compose notre projet, que Gradle nous fournira. Ajoutons là une dépendance sur le serveur web sur lequel nous allons expérimenter :
dependencies {
    // Web server
    implementation 'io.undertow:undertow-core:2.0.20.Final'
     // Use JUnit test framework
     testImplementation 'junit:junit:4.12'
}
Pour qu'une application Web fonctionne, nous avons absolument besoin d'un serveur Web sur lequel notre application sera hébergée. Il existe une grande variété de serveurs Web, mais les principaux sont : Tomcat, Jetty, Undertow. Cette fois, nous choisirons Undertow. Pour comprendre comment nous pouvons travailler avec notre serveur Web, allons sur le site officiel d' Undertow et accédez à la section documentation . Vous et moi avons connecté une dépendance à Undertow Core, nous sommes donc intéressés par la section sur ce même Core , c'est-à-dire le noyau, la base du serveur Web. Le moyen le plus simple consiste à utiliser l'API Builder pour 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();
}
Si nous exécutons le code, nous pouvons accéder à la ressource Web suivante :
Du HTTP au HTTPS - 5
Cela fonctionne simplement. Grâce à l'API Undertow Builder, nous ajoutons un écouteur HTTP à localhost et au port 8080. Cet écouteur reçoit les requêtes du navigateur web et renvoie la chaîne "Hello World" en réponse. Excellente application Web. Mais comme on le voit, nous utilisons le protocole HTTP, c'est-à-dire Ce type d'échange de données n'est pas sécurisé. Voyons comment s'effectuent les échanges à l'aide du protocole HTTPS.
Du HTTP au HTTPS - 6

Exigences pour HTTPS

Pour comprendre comment activer HTTPS, revenons à la spécification HTTPS : « RFC-2818 : HTTP Over TLS ». Selon la spécification, les données du protocole HTTPS sont transmises via les protocoles cryptographiques SSL ou TLS. Les gens sont souvent induits en erreur par les concepts de SSL et TLS. En fait, SSL a évolué et changé ses versions. Plus tard, TLS est devenu la prochaine étape dans le développement du protocole SSL. Autrement dit, TLS est simplement une nouvelle version de SSL. La spécification le dit : « SSL et son successeur TLS ». Ainsi, nous avons appris qu'il existe des protocoles cryptographiques SSL/TLS. SSL est l'abréviation de Secure Sockets Layer et se traduit par « couche de socket sécurisée ». La prise traduite de l'anglais est un connecteur. Les participants à la transmission de données sur un réseau utilisent les sockets comme interface de programmation (c'est-à-dire une API) pour communiquer entre eux sur le réseau. Le navigateur agit en tant que client et utilise un socket client, et le serveur qui reçoit une requête et émet une réponse utilise un socket serveur. Et c’est entre ces sockets que s’effectue l’échange de données. C'est pourquoi le protocole s'appelait à l'origine SSL. Mais le temps a passé et le protocole a évolué. Et à un moment donné, le protocole SSL est devenu le protocole TLS. TLS est l'abréviation de Transport Layer Security. Le protocole TLS, quant à lui, est basé sur la spécification du protocole SSL version 3.0. Le protocole TLS fait l'objet d'articles et de critiques séparés, j'indiquerai donc simplement les documents que je trouve intéressants : En bref, la base du HTTPS est la prise de contact TLS et la vérification de « l'identité du serveur » (c'est-à-dire l'identification du serveur) à l'aide de son certificat numérique. C'est important. Souvenons-nous-en, parce que... Nous reviendrons sur ce fait plus tard. Ainsi, plus tôt, nous avons utilisé HttpListener pour indiquer au serveur comment fonctionner via le protocole HTTP. Si dans l'exemple ci-dessus nous avons ajouté un HttpListener pour fonctionner sur HTTP, alors pour travailler sur HTTPS, nous devons ajouter un HttpsListener :
Du HTTP au HTTPS - 7
Mais pour l'ajouter, nous avons besoin de SSLContext. Fait intéressant, SSLContext n'est pas une classe d'Undertow, mais javax.net.ssl.SSLContext. La classe SSLContext fait partie de ce que l'on appelle « Java Secure Socket Extension » (JSSE) - une extension Java destinée à assurer la sécurité de la connexion Internet. Cette extension est décrite dans le « Guide de référence Java Secure Socket Extension (JSSE) ». Comme vous pouvez le voir dans la partie introductive de la documentation, JSSE fournit un cadre et une implémentation Java des protocoles SSL et TLS. Comment obtenons-nous le SSLContext ? Ouvrez le JavaDoc SSLContext et recherchez la méthode getInstance . Comme vous pouvez le voir, pour obtenir le SSLContext, nous devons spécifier le nom "Secure Socket Protocol". La description des paramètres indique que ces noms se trouvent dans la « Java Cryptography Architecture Standard Algorithm Name Documentation ». Par conséquent, suivons les instructions et passons à la documentation. Et on voit qu’on peut choisir entre SSL et TLS :
Du HTTP au HTTPS - 8
Nous comprenons maintenant que nous devons créer le SSLContext comme suit :
public SSLContext getSSLContext() {
	// 1. Получаем контекст, в рамках которого будем работать по TLS протоколу
	SSLContext context = null;
	try {
		context = SSLContext.getInstance("TLS");
	} catch (NoSuchAlgorithmException e) {
		throw new IllegalStateException(e);
	}
	return context;
}
Après avoir créé un nouveau contexte, nous rappelons que SSLContext a été décrit dans le « Java Secure Socket Extension (JSSE) Reference Guide ». Nous lisons et voyons que « Un SSLContext nouvellement créé doit être initialisé en appelant la méthode init ». Autrement dit, créer un contexte ne suffit pas. Il doit être initialisé. Et c'est logique, car concernant la sécurité, nous vous avons seulement dit que nous souhaitions utiliser le protocole TLS. Pour initialiser le SSLContext, nous devons fournir trois éléments : KeyManager, TrustManager, SecureRandom.
Du HTTP au HTTPS - 9

Gestionnaire de clés

KeyManager est un gestionnaire de clés. Il est responsable du « justificatif d’authentification » à fournir à quelqu’un qui nous contacte. Les informations d'identification peuvent être traduites par identité. L'identité est nécessaire pour que le client soit sûr que le serveur est bien celui qu'il prétend être et qu'il peut lui faire confiance. Qu’est-ce qui servira d’identification ? On s’en souvient, l’identité du serveur est vérifiée par le certificat numérique du serveur. Ce processus peut être représenté comme suit :
Du HTTP au HTTPS - 10
De plus, le « JSSE Reference Guide: How SSL Works » indique que SSL utilise une « cryptographie asymétrique », ce qui signifie que nous avons besoin d'une paire de clés : une clé publique et une clé privée. Puisque nous parlons de cryptographie, la « Java Cryptography Architecture » (JCA) entre en jeu. Oracle fournit un excellent document sur cette architecture : « Java Cryptography Architecture (JCA) Reference Guide ». De plus, vous pouvez lire un bref aperçu de JCA sur JavaRush : " Architecture de cryptographie Java : première connaissance ". Ainsi, pour initialiser le KeyManager, nous avons besoin d’un KeyStore, qui stockera le certificat de notre serveur. Le moyen le plus courant de créer un magasin de clés et de certificats est l'utilitaire keytool, inclus avec le JDK. Un exemple peut être vu dans la documentation JSSE : " Création d'un magasin de clés à utiliser avec JSSE ". Nous devons donc utiliser l'utilitaire KeyTool pour créer un magasin de clés et y écrire le certificat. Fait intéressant, la génération de clé était auparavant spécifiée à l'aide de -genkey, mais il est désormais recommandé d'utiliser -genkeypair. Nous devrons définir les éléments suivants :
  • alias : Alias ​​ou simplement le nom sous lequel l'entrée sera enregistrée dans Keystore
  • keyalg : Algorithme de chiffrement de clé. Choisissons l'algorithme RSA, qui est essentiellement une solution standard pour notre objectif.
  • keysize : Taille de la clé (en bits). La taille minimale recommandée est de 2048, car... une taille plus petite a déjà été fissurée. Vous pouvez en savoir plus ici : " un certificat SSL en 2048 bits ".
  • dname : Nom distinctif, nom distinctif.
Il est important de comprendre que la ressource demandée (par exemple, https://localhost) lui sera comparée. C'est ce qu'on appelle la « correspondance du sujet cn ».
  • validité : Durée en jours pendant laquelle le certificat généré est valide, soit valide.
  • ext : Extension de certificat spécifiée dans « Extensions nommées ».
Pour les certificats auto-signés (c'est-à-dire pour les certificats créés indépendamment), vous devez spécifier les extensions suivantes :
  • -ext san:critical=dns:localhost,ip:127.0.0.1 > pour effectuer une correspondance de sujet par SubjectAlternativeName
  • -ext bc=ca:false > pour indiquer que ce certificat n'est pas utilisé pour signer d'autres certificats
Exécutons la commande (exemple pour le système d'exploitation Windows) :
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
Parce que le fichier sera créé, assurez-vous que vous disposez de tous les droits pour créer le fichier. Vous verrez également probablement des conseils comme celui-ci :
Du HTTP au HTTPS - 11
Ici on nous dit que JKS est un format propriétaire. Propriétaire signifie qu'il s'agit de la propriété privée des auteurs et qu'il est destiné à être utilisé uniquement en Java. Lorsque vous travaillez avec des utilitaires tiers, un conflit peut survenir, c'est pourquoi nous sommes avertis. De plus, nous pouvons recevoir l'erreur : The destination pkcs12 keystore has different storepass and keypass. Cette erreur se produit car les mots de passe de l'entrée dans le magasin de clés et du magasin de clés lui-même sont différents. Comme le dit la documentation de keytool , "Par exemple, la plupart des outils tiers exigent que storepass et keypass dans un magasin de clés PKCS #12 soient identiques." Nous pouvons spécifier la clé nous-mêmes (par exemple, -destkeypass Entrypassw). Mais il vaut mieux ne pas enfreindre les exigences et définir le même mot de passe. L'importation pourrait donc ressembler à ceci :
keytool -importkeystore -srckeystore C:/keystore.jks -destkeystore C:/keystore.jks -deststoretype pkcs12
Exemple de réussite :
Du HTTP au HTTPS - 12
Pour exporter le certificat vers un fichier, vous pouvez exécuter :
keytool -export -alias ssl -storepass passw0rd -file C:/server.cer -keystore C:/keystore.jks
De plus, nous pouvons obtenir le contenu du Keystore comme ceci :
keytool -list -v -keystore C:/keystore.jks -storepass passw0rd
Génial, nous avons maintenant un magasin de clés contenant le certificat. Vous pouvez maintenant l'obtenir à partir du code :
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);
	}
}
S'il existe un KeyStore, alors nous pouvons initialiser le KeyManager :
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);
	}
}
Notre premier objectif a été atteint. Reste à comprendre ce qu'est TrustManager. TrustManager est décrit dans la documentation JSSE dans la section « L'interface TrustManager ». Il est très similaire à KeyManager, mais son but est de vérifier si la personne qui demande la connexion est digne de confiance. Pour parler franchement, c'est le KeyManager inversé =) Nous n'avons pas besoin de TrustManager, nous passerons donc null. Un TrustManager par défaut sera alors créé qui ne vérifiera pas l'utilisateur final qui fait des demandes à notre serveur. La documentation le dit : "l'implémentation par défaut sera utilisée". Idem avec SecureRandom. Si nous spécifions null, l'implémentation par défaut sera utilisée. Rappelons simplement que SecureRandom est une classe JCA et est décrite dans la documentation JCA dans la section « La classe SecureRandom ». Au total, la préparation prenant en compte toutes les méthodes décrites ci-dessus peut ressembler à ceci :
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);
	}
Il ne reste plus qu'à démarrer le serveur :
// 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();
}
Cette fois, notre serveur sera disponible à l'adresse suivante. https://localhost:443 Cependant, nous recevrons toujours une erreur indiquant que cette ressource n'est pas fiable :
Du HTTP au HTTPS - 13
Voyons quel est le problème avec le certificat et que faire à ce sujet.
Du HTTP au HTTPS - 14

Gestion des certificats

Ainsi, notre serveur est déjà prêt à fonctionner via HTTPS, mais le client ne lui fait pas confiance. Pourquoi? Jetons un coup d'oeil :
Du HTTP au HTTPS - 15
La raison en est que ce certificat est un certificat auto-signé. Un certificat SSL auto-signé fait référence à un certificat de clé publique émis et signé par la même personne qu'il identifie. Autrement dit, il n’a été délivré par aucune autorité de certification respectée (CA, également connue sous le nom d’autorité de certification). L'autorité de certification agit en tant que fiduciaire et ressemble à un notaire dans la vie de tous les jours. Il assure que les certificats qu'il délivre sont fiables. Le service de délivrance de certificats par ces autorités de certification est payant, personne n'a donc besoin de perte de confiance et de risques de réputation. Par défaut, plusieurs autorités de certification sont approuvées. Cette liste est modifiable. Et chaque système d'exploitation possède sa propre gestion de la liste des autorités de certification. Par exemple, la gestion de cette liste sous Windows peut être lue ici : « Gérer les certificats racines de confiance sous Windows ». Ajoutons le certificat à ceux de confiance comme indiqué dans le message d'erreur. Pour cela, commencez par télécharger le certificat :
Du HTTP au HTTPS - 16
Sous OS Windows, appuyez sur Win+R et exécutez mmcpour appeler la console de contrôle. Ensuite, appuyez sur Ctrl+M pour ajouter la section « Certificats » à la console actuelle. Ensuite, dans la sous-section « Autorités de certification racine de confiance », nous exécuterons Действия / Все задачи / Импорт. Importons le fichier téléchargé précédemment dans le fichier. Le navigateur s'est peut-être souvenu de l'état de confiance passé du certificat. Par conséquent, avant d'ouvrir la page, vous devez redémarrer le navigateur. Par exemple, dans Google Chrome, dans la barre d'adresse, vous devez exécuter chrome://restart. Sous OS Windows, vous pouvez également utiliser l'utilitaire pour afficher les certificats certmgr.msc:
Du HTTP au HTTPS - 17
Si nous avons tout fait correctement, nous verrons un appel réussi vers notre serveur via HTTPS :
Du HTTP au HTTPS - 18
Comme vous pouvez le constater, le certificat est désormais considéré comme valide, la ressource est disponible et il n'y a aucune erreur.
Du HTTP au HTTPS - 19

Conclusion

Nous avons donc compris à quoi ressemble le schéma d'activation du protocole HTTPS sur un serveur Web et ce qui est nécessaire pour cela. J'espère qu'à ce stade, il est clair que la prise en charge est fournie par l'interaction de l'architecture de cryptographie Java (JCA), responsable de la cryptographie, et de l'extension Java Secure Socket (JSSE), qui fournit l'implémentation TLS côté Java. Nous avons vu comment l'utilitaire keytool inclus dans le JDK est utilisé pour travailler avec le magasin de clés et de certificats KeyStore. De plus, nous avons réalisé que HTTPS utilise les protocoles SSL/TLS pour la sécurité. Pour renforcer cela, je vous conseille de lire d’excellents articles sur ce sujet : Espérons qu’après ce petit examen, HTTPS deviendra un peu plus transparent. Et si vous devez activer HTTPS, vous pouvez facilement comprendre les termes de la documentation de vos serveurs et frameworks d'applications. #Viacheslav
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION