1. Contexte
HTTP est un protocole public qui permet la lisibilité du transfert de contenu, et les données du client et du serveur sont complètement transmises via le texte en clair. Dans ce contexte, la totalité des données Internet qui repose sur le protocole HTTP est transparente, ce qui entraîne d'excellents risques de sécurité des données. Il y a deux idées pour résoudre ce problème:
Le premier type a une gamme plus large d'applications en réalité que ce que l'on imagine. Les deux parties échangent des touches hors ligne et le client utilise un texte chiffré lors de l'envoi de données, qui est transmise sur Internet via le protocole HTTP transparent. Après avoir reçu la demande, le serveur décrypte et obtient le texte brut de la manière convenue. Même si ce type de contenu est détourné, cela n'a pas d'importance, car les tiers ne connaissent pas leurs méthodes de cryptage et de décryptage. Cependant, cette approche est trop spéciale, et le client et le serveur doivent se soucier de cette logique spéciale de cryptage et de décryptage.
Le deuxième type de côté C / S peut ne pas se soucier de la logique spéciale ci-dessus. Ils croient que l'envoi et la réception sont en clair parce que la partie de chiffrement et de décryptage a été traitée par le protocole lui-même.
À en juger par les résultats, il ne semble pas y avoir de différence entre les deux solutions, mais du point de vue des ingénieurs logiciels, la différence est très énorme. Étant donné que le premier type nécessite que le système commercial développe la fonction de chiffrement et de décryptage de la réponse, et la clé interactive hors ligne est requise, le deuxième type n'a pas le volume de développement.
HTTPS est la forme de sécurité la plus populaire pour HTTP, créée pour la première fois par Netscape. Dans https, les URL commencent par https: // au lieu de http: //. Lorsque HTTPS est utilisé, toutes les demandes et réponses HTTP sont chiffrées avant d'être envoyées au réseau, qui est implémentée sur la couche SSL.
2. Méthode de chiffrement
Les données de texte brut sont chiffrées via la couche SSL, puis envoyées à Internet pour transmettre, ce qui résout le problème de sécurité des données d'origine du protocole HTTP. D'une manière générale, les méthodes de cryptage des données sont divisées en cryptage symétrique et en cryptage asymétrique.
2.1 cryptage symétrique
Le chiffrement symétrique fait référence à l'utilisation de la même clé que le cryptage et le décryptage. Les algorithmes communs incluent le DES et les AES, etc., et le temps d'algorithme est lié à la longueur de clé.
Le plus grand inconvénient des clés symétriques est qu'ils doivent maintenir un grand nombre de clés symétriques et nécessiter un échange hors ligne. Pour rejoindre un réseau avec N entités, des clés N (N-1) sont nécessaires.
2.2 cryptage asymétrique
Le chiffrement asymétrique fait référence aux méthodes de chiffrement basées sur des clés publiques / privées. Les algorithmes communs comprennent le RSA, qui se cryport généralement plus lent que le cryptage symétrique.
Le cryptage symétrique a une étape de plus que le cryptage asymétrique, c'est-à-dire pour obtenir la clé publique du serveur, plutôt que la clé maintenue par chacun.
L'algorithme de chiffrement entier est basé sur une certaine théorie des nombres, et l'effet est que le résultat de cryptage est irréversible. Autrement dit, ce n'est que par la clé privée que le texte chiffré crypté par la clé publique peut être déchiffré.
Dans cet algorithme, le nombre de clés dans l'ensemble du réseau est considérablement réduit et chaque personne n'a besoin que de maintenir une paire de clés d'entreprise. Autrement dit, dans le réseau de n entités, le nombre de clés est 2n.
L'inconvénient est qu'il fonctionne lentement.
2.3 cryptage hybride
Il y a une scène dans le film de Stephen Chow "God of Cookery" où les enfers sont dans un point chaud, se disputant la question de la division des châssis entre les crevettes pipi et les boules de bœuf. Le dieu de la nourriture a dit: "C'est vraiment gênant. Il est mélangé pour faire une balle de bœuf pipi, stupide!"
L'avantage du cryptage symétrique est sa vitesse rapide, et l'inconvénient est qu'il nécessite un échange de clés. L'avantage du cryptage asymétrique est qu'il ne nécessite pas de clé interactive, et l'inconvénient est qu'il est à vitesse lente. Mélangez-le simplement et utilisez-le bien.
Le chiffrement hybride est exactement la méthode de chiffrement utilisée par le protocole HTTPS. La clé symétrique est échangée d'abord par le cryptage asymétrique, puis les données sont transmises via la clé symétrique.
Étant donné que la quantité de transmission des données est beaucoup plus grande que la quantité de données utilisées pour l'échange de clés au début de l'établissement d'une connexion, l'impact des performances du cryptage asymétrique peut être essentiellement négligeable et améliore en même temps l'efficacité.
3. HTTPS Handshake
On peut voir que sur la base du protocole HTTP d'origine, HTTPS a ajouté un traitement de couche de sécurité:
4. Prise en charge de HttpClient pour le protocole HTTPS
4.1 Obtenir un dispositif de vérification de vérification du nom et de nom de domaine SSL
En tant qu'ingénieur logiciel, ce qui nous intéresse, c'est comment le "protocole HTTPS" est implémenté dans le code? Pour explorer le mystère du code source httpclient, tout commence par HttpClientBuilder.
Public CloseableHttpClient build () {// omettre un code httpclientConnectionManager ConnManagerCopy = this.ConnManager; // Si le gestionnaire de pool de connexion est spécifié, utilisez celui spécifié, sinon créez un nouveau défaut if (ConnManagerCopy == NULL) {LayeredConnectionSocketFactory SSLSocketFactoryCopy = this.SSLSocketFactory; if (sslSocketFactoryCopy == null) {// Si la variable d'environnement d'utilisation est activée, la version HTTPS et le contrôle de mot de passe se lisent à partir de la variable d'environnement Final String [] SupportEdProtoCols = SystemProperties? Split (System.GetProperty ("https.protoCols")): null; String final [] supportCiPhersites = systemProperties? Split (System.GetProperty ("https.ciphersuites")): null; // Si elle n'est pas spécifiée, utilisez le validateur de nom de domaine par défaut et il vérifiera si le nom de domaine correspond au certificat renvoyé par le serveur dans la session SSL HOSTNAMEVERIFIER HOSTNAMEVERIERIERCOPY = this.hostnameverifier; if (hostNameVeriFierCopy == null) {hostNameVierrierCopy = new DefaulThostNameverifier (PublicSuffixMatterCopy); } // Si SSLContext est formulé, une usine de connexion SSL personnalisée est générée, sinon l'usine de connexion par défaut est utilisée if (sslContext! = Null) {sslSocketFactoryCopy = new SSLConnectionSocketFactory (SSLContext, supportEdProtoCols, supportEdCiPhersuites, hostnameverifierCopy); } else {if (systemProperties) {sslsocketfactoryCopy = new sslConnectionSocketFactory ((sslSocketFactory) sslSocketFactory.getDefault (), supportEdProtoCols, supportEdCiPhersuittes, hostNameverifileCopy); } else {sslSocketFactoryCopy = new SSLConnectionSocketFactory (sslContext.CreateDefault (), HostNameVeriFierCopy); }}} // Enregistrez l'usine de connexion SSL dans le gestionnaire de pool de connexion. Lorsqu'une connexion HTTPS est nécessaire, la connexion SSL sera produite en fonction de la connexion SSL ci-dessus @SuppressWarnings ("Resource") Final PooringHttpClientConnectionManager PoolingMgr = New PoorlingHttpClientConnectionManager (RegistryBuilder. PlainConnectionSocketFactory.getSocketFactory ()) .Register ("https", sslsocketfactoryCopy) .build (), null, null, dnsResolver, conntimetolive: ConntimétoliveTimeUnit! = Null? // omettre certains codes}}Le code ci-dessus crée un SSLConnectionSocketFactory SSLConnectionSocketFactory et le registre dans le gestionnaire de pool de connexions pour la production ultérieure de connexions SSL. Référence pour la mise en commun des connexions: http://www.vevb.com/article/141015.htm
Ici, plusieurs composants clés sont utilisés lors de la configuration de SSLConnectionSocketFactory, du nom de nom de domaine HostNameVerifier et du contexte SSLContext.
Parmi eux, HostNameVerifier est utilisé pour vérifier si le certificat de serveur correspond au nom de domaine. Il existe plusieurs implémentations. DefaulThostNameverifier utilise les règles de vérification par défaut, en remplaçant le BrowserCompathostNameverifier et StricThostNameverifier dans la version précédente. NOOPHOSTNAMEVERIFIER remplace AllowhostNameverifier, en utilisant une stratégie de ne pas vérifier le nom de domaine.
Notez qu'il existe des différences ici, BrowserCompathostNameVerrifier peut correspondre aux noms de sous-domaine à plusieurs niveaux, et "* .foo.com" peut correspondre "abfoo.com". StricThostNameverifier ne peut pas correspondre aux noms de sous-domaine à plusieurs niveaux, uniquement à "a.foo.com".
Après 4,4, HttpClient a utilisé le nouveau Defaulthostnameverifier pour remplacer les deux stratégies ci-dessus, et n'a conservé qu'une stratégie stricte et un stricthostnameverifier. Étant donné que des stratégies strictes sont les stratégies de IE6 et JDK elle-même, les stratégies non strictes sont les stratégies de Curl et Firefox. C'est-à-dire que l'implémentation par défaut httpclient ne prend pas en charge la stratégie de correspondance du nom de sous-domaine à plusieurs niveaux.
SSLContext stocke les informations clés liées à la clé, qui est directement liée à l'entreprise et est très importante. Ceci doit être analysé séparément plus tard.
4.2 Comment obtenir une connexion SSL
Comment obtenir une connexion à partir d'un pool de connexion? Ce processus a été analysé dans les articles précédents. Je ne l'analyserai pas ici. Veuillez vous référer à la connexion: //www.vevb.com/article/141015.htm.
Après avoir obtenu une connexion à partir du pool de connexion, si la connexion n'est pas à l'état établi, vous devez d'abord établir la connexion.
Le code de la partie DefaulthTTPClientConnectionOpeclator est:
Public Void Connect (Final ManagedHTTPClientConnection Conn, Final Httphost Host, Final IneTSocketAddress localAddress, Final int connectTimeout, final socketconfig socketconfig, final httpcontext context) lance ioexception {// auparavant, l'implémentation du pool de connexion HTTPS a été enregistrée dans HTTPClientBuilder. Ici, la recherche obtient la mise en œuvre de HTTPS, c'est-à-dire SSLConnectionSocketFactory Final Lookup <ConnectionsOcketFactory> registry = getSocketFactoryRegistry (context); Connections FinalSocketFactory SF = Registry.lookup (host.getschememeName ()); if (sf == null) {lancez un nouveau non supporté en voûte (host.getschememeName () + "Le protocole n'est pas pris en charge"); } // Si l'adresse est dans le formulaire IP, il peut être utilisé directement, sinon utilisez l'analyseur DNS pour analyser l'IP correspondant au nom de domaine pour obtenir le final inetAddress [] adresses = host.getAddress ()! = Null? new InetAddress [] {host.getAddress ()}: this.dnsResolver.Resolve (host.GethostName ()); Final int port = this.schemeportresolver.resolve (hôte); // Un nom de domaine peut correspondre à plusieurs IP et essayer de se connecter pour (int i = 0; i <adresse booléen final dernier = i == adresses.length - 1; // Ceci est juste une prise générée, et il n'y a pas de connexion à socket sock = sf.createSocket (contexte); // Définissez certains paramètres de couche TCP sock.SetSoTimeout (socketConfig.getsoTimeout ()); sock.setReUSEADdress (socketconfig.issoreusEaddress ()); sock.setTCPNodeLay (socketconfig.istcpNodelay ()); sock.setkeepalive (socketconfig.issokeepalive ()); if (socketconfig.getrcvbufSize ()> 0) {sock.setReceiveBuffeSize (socketconfig.getrcvbufSize ()); } if (socketconfig.getsndbufSize ()> 0) {sock.setsendBuffeSize (socketconfig.getsndbufSize ()); } final int linger = socketconfig.getsolinger (); if (linger> = 0) {sock.setsolinger (true, linger); } conn.bind (sock); Final IneTsocketAddress RemoteAddress = new IneTSocketAddress (adresse, port); if (this.log.isdebugeNabled ()) {this.log.debug ("Connexion à" + remotEaddress); } essayez {// Établissez une connexion via sslconnectionsocketfactory et liez à Conn sock = sf.connectsocket (connectTimeout, sock, host, remotEaddress, localAddress, context); Conn.bind (chaussette); if (this.log.isdebugeNabled ()) {this.log.debug ("connexion établi" + Conn); } retour; } // omettre un code}}Dans le code ci-dessus, nous voyons que les travaux de préparation sont avant d'établir une connexion SSL. Il s'agit d'un processus général, et il en va de même pour les connexions HTTP ordinaires. Où reflète le processus spécial de la connexion SSL?
Le code source de SSLConnectionSocketFactory est le suivant:
@Override Public Socket ConnectSocket (Final int connectTimeout, final socket socket, final httphost host, final InetsocketAddress remoteaddress, final InetsocketAddress localAddress, final httpcontext context) lève ioException {args.notnull (hôte, "http hôte"); Args.notnull (Remoteaddress, "adresse distante"); socket final socket = socket! = null? Socket: CreateSocket (contexte); if (localAddress! = null) {sock.bind (localAddress); } essayez {if (connectTimeout> 0 && sock.getsoTimeout () == 0) {sock.SetSoTimeout (connectTimeout); } if (this.log.isdebugeNabled ()) {this.log.debug ("Connecter le socket à" + Remoteaddress + "avec timeout" + connectTimeout); } // Créer une connexion sock.connect (Remoteaddress, connectTimeout); } catch (final ioException ex) {try {sock.close (); } catch (final ioException ignore) {} throw ex; } // S'il est actuellement SSLSocket, effectuez une vérification de la poignée de main et du nom de domaine SSL if (sock instanceof sslsocket) {final sslsocket sslsock = (sslsocket) sock; this.log.debug ("Démarrage de la poignée de main"); sslsock.starthandshake (); VerifyHostName (sslsock, host.GethostName ()); chaussette de retour; } else {// Si ce n'est pas SSLSocket, enveloppez-le comme SSLSocket renvoie CreateLayeredSocket (sock, host.GethostName (), Remoteaddress.getport (), context); }} @Override Public Socket CreateLayeredSocket (socket final socket, cible de chaîne finale, port final, contexte final httpcontex this.socketfactory.createSocket (socket, cible, port, true); // Si la version du protocole de couche SSL et l'algorithme de chiffrement sont formulés, utilisez celui spécifié, sinon la valeur par défaut (supportDprotoCols! = Null) {sslsock.setEnabledProtoCols (supportDprotocols); } else {// Si les protocoles pris en charge ne sont pas explicitement définis, supprimez toutes les versions de protocole SSL Final String [] AllProtoCols = sslsock.getEnabledProtoCols (); Final List <string> ENableDProtoCols = new ArrayList <string> (allProtoCols.Length); pour (Final String Protocol: AllProtoCols) {if (! protoCol.startswith ("ssl")) {activésprotocols.add (protocole); }} if (! EnabledProtoCols.Isempty ()) {sslsock.setEnabledProtoCols (activésprotocols.toArray (new String [ENabledProtoCols.size ()])); }} if (supportEdCiPhersuites! = null) {sslsock.setEnabledCiPhersites (supportEdCiPhersites); } if (this.log.isdebugeNabled ()) {this.log.debug ("ProtoCols activé:" + arrays.aslist (sslsock.getEnabledProtoCols ())); this.log.debug ("Cipher Suites:" + arrays.aslist (sslsock.getEnabledCiphersuites ())); } PrepareSocket (sslsock); this.log.debug ("Démarrage de la poignée de main"); // SSL Connection Handshake sslsock.starthandShake (); // Une fois la poignée de main réussie, vérifiez si le certificat retourné est conforme au nom de domaine VerifyHostName (SSLSock, Target); retour sslsock; }Comme on peut le voir, pour une communication SSL. Tout d'abord, établissez une connexion de socket normale, puis effectuez une poignée de main SSL, puis vérifiez la cohérence du certificat et du nom de domaine. L'opération suivante consiste à communiquer via SSLSocketImpl. Les détails du protocole sont reflétés dans la classe SSLSocketImpl, mais cette partie du code JDK n'est pas open source. Ceux qui sont intéressés peuvent télécharger le code source OpenJDK correspondant pour poursuivre l'analyse.
5. Résumé de cet article
D'accord, ce qui précède est l'intégralité du contenu de cet article. J'espère que le contenu de cet article a une certaine valeur de référence pour l'étude ou le travail de chacun. Si vous avez des questions, vous pouvez laisser un message pour communiquer. Merci pour votre soutien à wulin.com.