Consejo 96 de Java: utilice HTTPS en su código de cliente Java

Si alguna vez ha intentado implementar una comunicación segura entre un cliente Java y un servidor HTTPS (Protocolo seguro de transferencia de hipertexto), probablemente haya descubierto que la java.net.URLclase estándar no es compatible con el protocolo HTTPS. La implementación del lado del servidor de esa ecuación es bastante sencilla. Casi todos los servidores web disponibles en la actualidad proporcionan un mecanismo para solicitar datos mediante HTTPS. Una vez que haya configurado su servidor web, cualquier navegador puede solicitar información segura de su servidor simplemente especificando HTTPS como el protocolo para la URL. Si aún no tiene un servidor HTTPS configurado, puede probar su código de cliente con casi cualquier página web HTTPS en Internet. La sección de Recursos contiene una breve lista de candidatos que puede utilizar para ese propósito.

Sin embargo, desde la perspectiva del cliente, la simplicidad de la S al final del conocido HTTP es engañosa. En realidad, el navegador está haciendo una cantidad considerable de trabajo entre bastidores para garantizar que nadie haya manipulado o supervisado la información que solicitó. Resulta que el algoritmo para realizar el cifrado de HTTPS está patentado por RSA Security (al menos durante unos meses más). El uso de ese algoritmo ha sido autorizado por los fabricantes de navegadores, pero Sun Microsystems no ha autorizado su inclusión en la URLimplementación estándar de la clase Java . Como resultado, si intenta construir un URLobjeto con una cadena que especifica HTTPS como protocolo, se MalformedURLExceptionlanzará un .

Afortunadamente, para adaptarse a esa restricción, la especificación de Java proporciona la capacidad de seleccionar un controlador de flujo alternativo para la URLclase. Sin embargo, la técnica requerida para implementar eso es diferente, dependiendo de la máquina virtual (VM) que use. Para la máquina virtual compatible con JDK 1.1 de Microsoft, JView, Microsoft obtuvo la licencia del algoritmo y proporcionó un controlador de flujo HTTPS como parte de su wininetpaquete. Sun, por otro lado, ha lanzado recientemente Java Secure Sockets Extension (JSSE) para máquinas virtuales compatibles con JDK 1.2, en la que Sun también ha otorgado licencias y proporcionado un controlador de flujo HTTPS. Este artículo demostrará cómo implementar el uso de un controlador de flujo habilitado para HTTPS, utilizando JSSE y el wininetpaquete de Microsoft .

Máquinas virtuales compatibles con JDK 1.2

La técnica para utilizar máquinas virtuales compatibles con JDK 1.2 se basa principalmente en Java Secure Sockets Extension (JSSE) 1.0.1. Antes de que esa técnica funcione, debe instalar JSSE y agregarlo a la ruta de clase de la VM cliente en cuestión.

Una vez que haya instalado JSSE, debe establecer una propiedad del sistema y agregar un nuevo proveedor de seguridad al Securityobjeto de clase. Hay una variedad de formas de hacer ambas cosas, pero para los propósitos de este artículo, se muestra el método programático:

System.setProperty ("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); Security.addProvider (nuevo com.sun.net.ssl.internal.ssl.Provider ());

Después de realizar las dos llamadas de método anteriores, MalformedURLExceptionya no se lanzará llamando al siguiente código:

 URL url = nueva URL ("// [su servidor]"); 

Si se está conectando al puerto SSL estándar, 443, tiene la opción de agregar el número de puerto a la cadena URL. Sin embargo, si su servidor web está usando un puerto no estándar para el tráfico SSL, deberá agregar el número de puerto a su cadena de URL de la siguiente manera:

 URL url = nueva URL ("// [su servidor]: 7002"); 

Una advertencia de esa técnica se refiere a una URL que hace referencia a un servidor que tiene un certificado SSL no firmado o no válido. En ese caso, un intento de recuperar el flujo de entrada o salida del objeto de conexión de la URL arrojará un SSLExceptionmensaje con el mensaje "cadena de certificados de servidor no confiable". Si el servidor tiene un certificado firmado válido, no se lanzará ninguna excepción.

URL url = nueva URL ("// [su servidor]"); URLConnection con = URL.openConnection (); // Se lanza SSLException si el certificado del servidor no es válido con.getInputStream ();

La solución obvia a ese problema es obtener certificados firmados para su servidor. Sin embargo, una de las siguientes URL también puede proporcionar una solución: "Cambios en la extensión Java Secure Socket 1.0.2" (Sun Microsystems) o el foro Java Developer Connection de Sun.

Microsoft JView

Debido en parte a la disputa en curso entre Microsoft y Sun sobre la licencia de Java para su uso en plataformas Windows, Microsoft JView VM actualmente solo cumple con JDK 1.1. Por lo tanto, la técnica descrita anteriormente no funcionará para clientes que se ejecuten en JView, ya que JSSE requiere al menos una VM compatible con 1.2.2. Sin embargo, de forma bastante conveniente, Microsoft proporciona un controlador de flujo habilitado para HTTPS como parte del com.ms.net.wininetpaquete.

Puede configurar el controlador de flujo en un entorno JView llamando a un único método estático en la URLclase:

 URL.setURLStreamHandlerFactory (nuevo com.ms.net.wininet.WininetStreamHandlerFactory ()); 

Después de realizar la llamada al método anterior, el

MalformedURLException

ya no se lanzará llamando al siguiente código:

 URL url = nueva URL ("// [su servidor]"); 

Hay dos salvedades asociadas con esa técnica. Primero, de acuerdo con la documentación de JDK, el setURLStreamHandlerFactorymétodo se puede llamar como máximo una vez en una VM determinada. Los intentos posteriores de llamar a ese método arrojarán un Error. En segundo lugar, como es el caso de la solución 1.2 VM, debe tener cuidado al utilizar una URL que haga referencia a un servidor con un certificado SSL no firmado o no válido. Al igual que en el caso anterior, se producen problemas cuando se intenta recuperar el flujo de entrada o salida del objeto de conexión de la URL. Sin embargo, en lugar de lanzar un SSLException, el controlador de flujo de Microsoft lanza un estándar IOException.

URL url = nueva URL ("// [su servidor]"); URLConnection con = url.openConnection (); // IOException se lanza aquí si el certificado del servidor no es válido con.getInputStream ();

Nuevamente, la solución obvia a ese problema es intentar la comunicación HTTPS solo con servidores que tengan un certificado válido firmado. Sin embargo, JView ofrece otra opción. Inmediatamente antes de recuperar el flujo de entrada o salida del objeto de conexión de la URL, puede llamar setAllowUserInteraction(true)al objeto de conexión. Eso hará que JView muestre un mensaje advirtiendo al usuario que los certificados del servidor no son válidos, pero dándole la opción de continuar de todos modos. Sin embargo, tenga en cuenta que estos mensajes pueden ser razonables para una aplicación de escritorio, pero que aparezcan cuadros de diálogo en su servidor para cualquier otra cosa que no sea la depuración es probablemente inaceptable.

Nota: También puede llamar al setAllowUserInteraction()método en máquinas virtuales compatibles con JDK 1.2. Sin embargo, al usar la VM 1.2 de Sun (con la que se probó este código), no se muestran cuadros de diálogo incluso cuando esa propiedad se establece en verdadera.

URL url = nueva URL ("// [su servidor]"); URLConnection con = url.openConnection (); // hace que la VM muestre un diálogo cuando se conecta // a servidores que no son de confianza con.setAllowUserInteraction (true); con.getInputStream ();

El com.ms.net.wininetpaquete parece estar instalado y colocado en la ruta de clases del sistema de forma predeterminada en los sistemas Windows NT 4.0, Windows 2000 y Windows 9x. Además, según la documentación de Microsoft JDK, WinInetStreamHandlerFactoryes "... el mismo controlador que se instala de forma predeterminada cuando se ejecutan subprogramas".

Independencia de la plataforma

Aunque ambas técnicas que he descrito cubren la mayoría de las plataformas en las que se puede ejecutar su cliente Java, es posible que su cliente Java deba ejecutarse en máquinas virtuales compatibles con JDK 1.1 y JDK 1.2. "Escribe una vez, corre a cualquier parte", ¿recuerdas? Resulta que combinar esas dos técnicas para que se cargue el controlador apropiado según la máquina virtual es bastante sencillo. El siguiente código demuestra una forma de hacerlo:

String strVendor = System.getProperty ("java.vendor"); String strVersion = System.getProperty ("java.version"); // Asume una cadena de versión del sistema de la forma: //[major].[minor].[release] (p. Ej. 1.2.2) Double dVersion = new Double (strVersion.substring (0, 3)); // Si estamos ejecutando en un entorno MS, use el controlador de flujo de MS. if (-1 <strVendor.indexOf ("Microsoft")) {try {Class clsFactory = Class.forName ("com.ms.net.wininet.WininetStreamHandlerFactory"); if (null! = clsFactory) URL.setURLStreamHandlerFactory ((URLStreamHandlerFactory) clsFactory.newInstance ()); } catch (ClassNotFoundException cfe) {lanzar nueva excepción ("No se puede cargar el controlador de flujo de Microsoft SSL" + ". Verifique classpath." + cfe.toString ());} // Si la fábrica del controlador de flujo // ya se ha configurado correctamente // asegúrese de que nuestra bandera esté configurada y come el error catch (Error err) {m_bStreamHandlerSet = true;}} // Si estamos en un entorno Java normal // intenta utilizar el controlador JSSE. // NOTA: JSSE requiere 1.2 o mejor si no (1.2 <= dVersion.doubleValue ()) {System.setProperty ("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol "); try {// si tenemos el proveedor JSSE disponible, // y aún no se ha // configurado, agréguelo como un nuevo suministro a la clase Security. Class clsFactory = Class.forName ("com.sun.net.ssl.internal.ssl.Provider"); if ((null! = clsFactory) && (null == Security.getProvider ("SunJSSE"))) Security.addProvider ((Provider) clsFactory.newInstance ());} catch (ClassNotFoundException cfe) {throw new Exception ("No se puede cargar el controlador de flujo JSSE SSL." + "Verificar classpath." + cfe.toString ()); }}

¿Qué pasa con los applets?

Realizar una comunicación basada en HTTPS desde dentro de un subprograma parece una extensión natural de los escenarios descritos anteriormente. En realidad, es incluso más fácil en la mayoría de los casos. En las versiones 4.0 y posteriores de Netscape Navigator e Internet Explorer, HTTPS está habilitado de forma predeterminada para sus respectivas VM. Por lo tanto, si desea crear una conexión HTTPS desde el código de su subprograma, simplemente especifique HTTPS como su protocolo al crear una instancia de la URLclase:

 URL url = nueva URL ("// [su servidor]"); 

Si el navegador del cliente ejecuta el complemento Java 2 de Sun, existen limitaciones adicionales sobre cómo puede utilizar HTTPS. Puede encontrar una discusión completa sobre el uso de HTTPS con el complemento de Java 2 en el sitio web de Sun (consulte Recursos).

Conclusión

El uso del protocolo HTTPS entre aplicaciones puede ser una forma rápida y eficaz de obtener un nivel razonable de seguridad en su comunicación. Desafortunadamente, las razones por las que no se admite como parte de la especificación estándar de Java parecen ser más legales que técnicas. Sin embargo, con la llegada de JSSE y el uso del com.ms.net.winintpaquete de Microsoft , la comunicación segura es posible desde la mayoría de las plataformas con solo unas pocas líneas de código.

Matt Towers, un eBozo que se describe a sí mismo, dejó recientemente su puesto de desarrollo en Visio. Desde entonces, se unió a una empresa emergente de Internet, PredictPoint.com, en Seattle, Washington, donde trabaja como desarrollador Java a tiempo completo.

Más información sobre este tema

  • The source code zip file for this article contains the platform-independent code shown above implemented in a class called HttpsMessage. HttpsMessage is intended as a subclass to the HttpMessage class written by Jason Hunter, author of Java Servlet Programming (O'Reilly & Associates). Look for HttpsMessage in the upcoming second edition of his book. If you wish to use that class as intended, you'll need to download and install the com.oreilly.servlets package. The com.oreilly.servlets package and corresponding source code can be found on Hunter's Website

    //www.servlets.com

  • You can also download the source zip file

    //images.techhive.com/downloads/idge/imported/article/jvw/2000/06/httpsmessage.zip

  • Here are a few good Webpages for testing HTTPS communication:
  • //www.verisign.com/
  • //happiness.dhs.org/
  • //www.microsoft.com
  • //www.sun.com
  • //www.ftc.gov
  • More information on the JSSE as well as the downloadable bits and installation instructions can be found on Sun's Website

    //java.sun.com/products/jsse/.

  • A description of how to use some JSSE services, including the technique described above, can be found in "Secure Networking in Java" by Jonathan Knudsen on the O'Reilly Website

    //java.oreilly.com/bite-size/java_1099.html

  • More information on WininetStreamHandlerFactory class can be found in the Microsoft JSDK documentation

    //www.microsoft.com/java/sdk/. In addition, the Microsoft knowledge base also publishes "PRBAllowing the URL class to access HTTPS in Applications"

    //support.microsoft.com/support/kb/articles/Q191/1/20.ASP

  • For more information on using HTTPS with the Java 2 plug-in, see "How HTTPS Works in Java Plug-In" on Sun's Website

    //java.sun.com/products/plugin/1.2/docs/https.html

Esta historia, "Java Tip 96: Use HTTPS en su código de cliente Java" fue publicada originalmente por JavaWorld.