1. Antecedentes
HTTP es un protocolo público que permite la legibilidad de la transferencia de contenido, y los datos del cliente y servidor se transmiten por completo a través del texto sin formato. En este contexto, todos los datos de Internet que se basan en el protocolo HTTP son transparentes, lo que brinda grandes riesgos de seguridad de datos. Hay dos ideas para resolver este problema:
El primer tipo tiene una gama más amplia de aplicaciones en la realidad de lo que se imaginó. Las dos partes intercambian claves fuera de línea, y el cliente usa un texto cifrado al enviar datos, que se transmite en Internet a través del protocolo HTTP transparente. Después de recibir la solicitud, el servidor descifra y obtiene el texto sin formato de la manera acordada. Incluso si este tipo de contenido está secuestrado, no importa, porque los terceros no conocen sus métodos de cifrado y descifrado. Sin embargo, este enfoque es demasiado especial, y tanto el cliente como el servidor deben preocuparse por esta lógica especial de cifrado y descifrado.
El segundo tipo de lado C/S puede no preocuparse por la lógica especial anterior. Creen que el envío y la recepción son de texto sin formato porque la parte de cifrado y descifrado ha sido procesada por el protocolo mismo.
A juzgar por los resultados, parece que no hay diferencia entre las dos soluciones, pero desde la perspectiva de los ingenieros de software, la diferencia es muy grande. Debido a que el primer tipo requiere que el sistema comercial desarrolle la función de cifrado y descifrado para la respuesta, y se requiere la clave interactiva fuera de línea, el segundo tipo no tiene el volumen de desarrollo.
HTTPS es la forma más popular de seguridad para HTTP, creada por primera vez por Netscape. En https, las URL comienzan con https: // en lugar de http: //. Cuando se usa HTTPS, todas las solicitudes y respuestas HTTP se cifran antes de enviarse a la red, que se implementa en la capa SSL.
2. Método de cifrado
Los datos de texto plano se encriptan a través de la capa SSL y luego se envían a Internet para transmitir, lo que resuelve el problema de seguridad de datos original del protocolo HTTP. En términos generales, los métodos de cifrado de datos se dividen en cifrado simétrico y cifrado asimétrico.
2.1 cifrado simétrico
El cifrado simétrico se refiere al uso de la misma clave que el cifrado y el descifrado. Los algoritmos comunes incluyen DES y AES, etc., y el tiempo de algoritmo está relacionado con la longitud de la clave.
La mayor desventaja de las claves simétricas es que necesitan mantener una gran cantidad de claves simétricas y requieren intercambio fuera de línea. Para unirse a una red con entidades N, se requieren claves N (N-1).
2.2 Cifrado asimétrico
El cifrado asimétrico se refiere a métodos de cifrado basados en claves públicas/privadas. Los algoritmos comunes incluyen RSA, que generalmente es más lento que el cifrado simétrico.
El cifrado simétrico tiene un paso más que el cifrado asimétrico, es decir, para obtener la clave pública del servidor, en lugar de la clave mantenida por cada una.
El algoritmo de cifrado completo se basa en una cierta teoría de números, y el efecto es que el resultado de cifrado es irreversible. Es decir, solo a través de la clave privada se puede descifrar el texto cifrado encriptado por la clave pública.
Bajo este algoritmo, el número de claves en toda la red se reduce considerablemente, y cada persona solo necesita mantener un par de claves de la empresa. Es decir, en la red de N entidades, el número de claves es 2n.
La desventaja es que funciona lentamente.
2.3 cifrado híbrido
Hay una escena en la película de Stephen Chow "God of Cookery", donde el inframundo está en un punto caliente, discutiendo sobre el tema de la división de chasis entre los camarones de orina y las bolas de carne. El dios de la comida dijo: "Es realmente problemático. ¡Está mezclado para hacer una pelota de carne de res, estúpido!"
La ventaja del cifrado simétrico es su velocidad rápida, y la desventaja es que requiere un intercambio de claves. La ventaja del cifrado asimétrico es que no requiere una clave interactiva, y la desventaja es que es una velocidad lenta. Simplemente mezclelo y úselo bien.
El cifrado híbrido es exactamente el método de cifrado utilizado por el protocolo HTTPS. La clave simétrica se intercambia primero a través del cifrado asimétrico, y luego los datos se transmiten a través de la clave simétrica.
Dado que la cantidad de transmisión de datos es mucho mayor que la cantidad de datos utilizados para intercambiar claves en la etapa temprana de establecer una conexión, el impacto en el rendimiento del cifrado asimétrico puede ser básicamente insignificante, y al mismo tiempo mejora la eficiencia.
3. HTTPS PANDSWAKE
Se puede ver que, según el protocolo HTTP original, HTTPS ha agregado el procesamiento de la capa de seguridad:
4. Soporte de HttpClient para el protocolo HTTPS
4.1 Obtener el dispositivo de verificación de nombres de dominio y fábrica de dominio SSL
Como ingeniero de software, ¿lo que nos importa es cómo se implementa el "Protocolo HTTPS" en el código? Para explorar el misterio del código fuente HTTPClient, todo comienza con HttpClientBuilder.
Public ClosableHttpClient Build () {// omitir algunos código httpClientConnectionManager connmanagercopy = this.connmanager; // Si se especifica el Administrador del grupo de conexión, use el especificado, de lo contrario, cree un nuevo if predeterminado (connmanagerCopy == null) {capaseredConnectionsocketFactory sslsocketFactoryCopy = this.sslsocketFactory; if (sslsocketFactoryCopy == null) {// Si la variable de entorno de uso está habilitada, ¿la versión HTTPS y el control de contraseña leen la cadena final [] SoportedProtocols = SystemProperties? Split (System.getProperty ("https.protocols")): nulo; String final [] SupportedCipHersuites = SystemProperties? Split (System.getProperty ("https.ciphersuites")): null; // Si no se especifica, use el validador de nombre de dominio predeterminado y verificará si el nombre de dominio coincide con el certificado devuelto por el servidor en la sesión SSL HostNameNeVerifier hostNameverifeCopy = this.hostNameverifier; if (hostnameverifiercopy == null) {hostnameveriferCopy = new DeFaUsThostNameverifier (PublicsUffixMatcherCopy); } // Si se formula SSLContext, se genera una fábrica de conexión SSL personalizada, de lo contrario, la fábrica de conexión predeterminada se usa si (SSLContext! = NULL) {SSLSocketFactoryCopy = new SslConnectionSockFactory (SSLContext, SupportedProtocols, SupportedCiPhersuteres, hostNameverifiercopy); } else {if (SystemProperties) {sslsocketFactoryCopy = new sslConnectionsocketFactory ((sslsocketFactory) sslsocketfactory.getDefault (), soportedprotocols, soportedciphersuites, hostnameverifiercopy); } else {sslsocketFactoryCopy = new SSLConnectionsocketFactory (sslContexts.CreateDefault (), hostnameverifepopy); }}} // Registre la fábrica de conexión SSL en el Administrador de grupos de conexión. Cuando se necesita una conexión HTTPS, la conexión SSL se producirá en función de la fábrica de conexión SSL anterior @SupessPressWarnings ("Resource") Final PoolingHttpClientConnectionManager PoolingMgr = New PoolingHttpClientConnectionManager (RegUdRistryer. <ConnectionSockory> Create (create (". .register ("https", sslsocketfactorycopy) .Build (), nulo, null, dnsresolver, conntimetolive, conntimeToliveTimeUnit! = NULL? // omitir algunos códigos}}El código anterior crea un SSLConnectionsocketFactory SSLConnectionsocketFactory y lo registra en el Administrador de grupos de conexión para la producción posterior de conexiones SSL. Referencia para la agrupación de conexión: http://www.vevb.com/article/141015.htm
Aquí, se utilizan varios componentes clave al configurar SSLConnectionsocketFactory, el nombre de host de validador del nombre de dominio y el contexto SSLContext.
Entre ellos, HostNameVerificador se utiliza para verificar si el certificado del servidor coincide con el nombre de dominio. Hay muchas implementaciones. DeFaUsThostNameVerificador utiliza las reglas de verificación predeterminadas, reemplazando el browserCompathostNameverifier y StricthostNameVerifier en la versión anterior. NoophostNameverificador reemplaza a ToilLhostNameverifier, utilizando una estrategia de no verificar el nombre de dominio.
Tenga en cuenta que hay algunas diferencias aquí, BrowserCompathostNameVerifier puede coincidir con los nombres de subdominios de nivel múltiple y "*.foo.com" puede igualar "abfoo.com". StricthostNameverificador no puede igualar los nombres de subdominios de nivel múltiple, solo con "a.foo.com".
Después de 4.4, HttpClient utilizó el nuevo DefaUsThostNameVerifier para reemplazar las dos estrategias anteriores, y solo retuvo una estrategia estricta y StricthostNameverifier. Debido a que las estrategias estrictas son las estrategias de IE6 y JDK en sí, las estrategias no rictamente son las estrategias de curl y Firefox. Es decir, la implementación predeterminada de HTTPClient no admite la estrategia de coincidencia de nombres de subdominios de nivel múltiple.
SSLContext almacena información clave relacionada con la clave, que está directamente relacionada con el negocio y es muy importante. Esto debe analizarse por separado más tarde.
4.2 Cómo obtener una conexión SSL
¿Cómo obtener una conexión de un grupo de conexión? Este proceso ha sido analizado en artículos anteriores. No lo analizaré aquí. Consulte la conexión: //www.vevb.com/article/141015.htm.
Después de obtener una conexión del grupo de conexión, si la conexión no está en el estado establecido, primero debe establecer la conexión.
El código de la parte DeFaulthtpClientConnectionOperator es:
Public void Connect (Final ManagedHttpClientConnection Conn, HTTPHOST final Host, INetSocketAddress FinalSaddress, Final Int ConnectTimeOut, Final SocketConfig SocketConfig, HTTPContext Context final) lanza IOException {// anteriormente, la implementación del grupo de conexión de HTTPS se registró en httpClientBuilder. Aquí, la búsqueda obtiene la implementación de https, es decir, sslconnectionsocketfactory lookup final <nectificationsockFactory> registry = getSockyFactoryRegistry (contexto); final ConnectionSocketFactory SF = Registry.LightUp (host.getSchemename ()); if (sf == null) {lanzar new UnsupportedSchemeException (host.getSchemename () + "El protocolo no es compatible"); } // Si la dirección es en forma de IP, se puede usar directamente, de lo contrario, use el analizador DNS para analizar la IP correspondiente al nombre de dominio para obtener el InetAddress final [] direcciones = host.getAddress ()! = nuevo inetaddress [] {host.getAddress ()}: this.dnsresolver.resolve (host.gethostName ()); final int port = this.schemeportresolver.resolve (host); // Un nombre de dominio puede corresponder a IP múltiple e intentar conectarse en orden para (int i = 0; i <direction.length; i ++) {la dirección final inetaddress = direcciones [i]; Final Boolean Last = i == direcciones. Length - 1; // Esto es solo un socket generado, y no hay conexión con Socket Sock = Sf.CreateSocket (contexto); // Establecer algunos parámetros de capa TCP Sock.SetSotimeOut (SocketConfig.getSotimeOut ()); SOCK.SETREUSEADDRESS (SocketConfig.issoreUseadDress ()); SOCK.SETTCPNODELAY (SocketConfig.istcpnodelay ()); SOCK.SetKeepAlive (SocketConfig.IssokeEpalive ()); if (SocketConfig.getRCVBufSize ()> 0) {Sock.SetReceiveBufferSize (SocketConfig.getRCVBufSize ()); } if (SocketConfig.getSndBufSize ()> 0) {Sock.SetSendBufferSize (SocketConfig.getSndBufSize ()); } final int linger = SocketConfig.getSolinger (); if (linger> = 0) {sock.setsolinger (true, linger); } conn.bind (calcetín); INetSocketAddress final Remoteaddress = nuevo inetSocketAddress (dirección, puerto); if (this.log.isdeBugeNabled ()) {this.log.debug ("Conectando a" + remotaaddress); } try {// Establezca una conexión a través de sslconnectionsocketFactory y se une a conn sock = sf.connectsocket (conectionTimeOut, sock, host, remoTeaddress, localaddress, context); Conn.bind (calcetín); if (this.log.isDebugeNabled ()) {this.log.debug ("Conexión establecida" + Conn); } devolver; } // omitir algún código}}En el código anterior, vemos que el trabajo de preparación es antes de establecer una conexión SSL. Este es un proceso general, y lo mismo es cierto para las conexiones HTTP ordinarias. ¿Dónde refleja el proceso especial de conexión SSL?
El código fuente de SSLConnectionSockyFactory es el siguiente:
@Override public Socket ConnectSocket (Final Int ConnecttimeOut, socket final Final Socket, httphost final host, INETSOCKECKECKECKECKECKEDDRESS, INetSocketAddress final Localaddress, contexto final HTTPContext) arroja ioexception {args.notnull (host, "http host"); Args.notnull (remoteaddress, "dirección remota"); SOCKE SOCKE SOCK = socket! = NULL? socket: createSocket (contexto); if (localaddress! = null) {sock.bind (localaddress); } try {if (ConnectTimeOut> 0 && Sock.getSotimeOut () == 0) {Sock.SetSoTimeOut (ConnectTimeOut); } if (this.log.isDebugeNabled ()) {this.log.debug ("Connecting Socket a" + remoTeaddress + "con Tiempo de espera" + ConnectTimeOut); } // Crea Connection Sock.Connect (remoTeadDress, ConnectTimeOut); } catch (final ioexception ex) {try {sock.close (); } catch (final IOException ignore) {} Throw Ex; } // Si actualmente es sslsocket, realice ssl manual y verificación de nombre de dominio si (sock instanceo de sslsocket) {sslsocket final sslsock = (sslsocket) sock; this.log.debug ("iniciando el apretón de manos"); sslsock.starthandShake (); VerifyHostName (sslsock, host.gethostName ()); calcetín de regreso; } else {// Si no es sslsocket, envuélvalo como sslsocket return createLayerDsocket (SOCK, host.getHostName (), remoTeadDress.getPort (), context); }} @Override public Socket CreateLayerDsocket (Final Socket Socket, Final String Target, Final Int Port, Final HttpContext Context) lanza ioexception {// envuelve un socket normal como sslsocket, sslsockory se genera en base a sslcontext en httpclientbuilder, que contiene el sslsocket sslsocket = (sslsocksksock = (sslcontext (sslContext en httpclientbuilder, que contiene la información clave final sslsockets = (sslslock = (sslCoinskek (sslCalket (sslCon this.socketfactory.createSocket (enchufe, objetivo, puerto, verdadero); // Si se formulan la versión del protocolo de capa SSL y el algoritmo de cifrado, use el especificado, de lo contrario el predeterminado if (SupportedProtocols! = NULL) {SSLSOCK.SetEnabledProtocols (SupportedProtocols); } else {// Si los protocolos compatibles no se establecen explícitamente, elimine todas las versiones de protocolo SSL String final [] allProtocols = sslsock.getenableprotocols (); Lista final <String> EndabledProTocols = new ArrayList <String> (allProTocols.length); for (protocolo de cadena final: allProtocols) {if (! }} if (! EndabledProTocols.isEmpty ()) {sslsock.SetEnabledProTocols (habilitableProTocols.toarray (new String [EndabledProtOcols.size ()]); }} if (SupportedCipHersuites! = NULL) {SSLSOCK.SetEnableCipHersuites (SupportedCipHersuites); } if (this.log.isdeBugeNabled ()) {this.log.debug ("Protocolos habilitados:" + arrays.aslist (sslsock.getEnabledProTocols ())); this.log.debug ("Suites de cifrado habilitado:" + matrices.aslist (sslsock.getEnabledCipHersuites ())); } PrepareSocket (sslsock); this.log.debug ("iniciando el apretón de manos"); // Conexión SSL HandShake sslsock.starthandShake (); // Después de que el apretón de manos sea exitoso, verifique si el certificado devuelto es consistente con el nombre de dominio VerifyHostName (SSLSock, Target); devolver sslsock; }Como se puede ver, para una comunicación SSL. Primero, establezca una conexión de socket normal, luego realice un apretón de manos SSL y luego verifique la consistencia del certificado y el nombre de dominio. La operación posterior es comunicarse a través de SSLSocketImpl. Los detalles del protocolo se reflejan en la clase SSLSocketImpl, pero esta parte del código JDK no es de código abierto. Los interesados pueden descargar el código fuente OpenJDK correspondiente para continuar el análisis.
5. Resumen de este artículo
De acuerdo, lo anterior es todo el contenido de este artículo. Espero que el contenido de este artículo tenga cierto valor de referencia para el estudio o el trabajo de todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse. Gracias por su apoyo a Wulin.com.