Podemos tener básicamente dos motivos para proteger una aplicación web:
Para cubrir estos agujeros, un sistema de seguridad se apoya en tres aspectos importantes:
Existen dos formas de seguridad que se pueden implantar en un sistema Web:
1. Seguridad declarativa
Aquella estructura de seguridad sobre una aplicación que es externa a dicha aplicación. Con ella, no tendremos que preocuparnos de gestionar la seguridad en ningún servlet, página JSP, etc, de nuestra aplicación, sino que el propio servidor Web se encarga de todo. Así, ante cada petición, comprueba si el usuario se ha autentificado ya, y si no le pide login y password para ver si puede acceder al recurso solicitado. Todo esto se realiza de forma transparente al usuario. Mediante el descriptor de la aplicación principalmente (fichero web.xml en Tomcat), comprueba la configuración de seguridad que queremos dar.
2. Seguridad programada
Mediante la seguridad programada, son los servlets y páginas JSP quienes, al menos parcialmente, controlan la seguridad de la aplicación.
Veremos ahora, resumidos, los distintos tipos de autentificación que podemos emplear:
1. Autentificación basic
Vimos con HTTP que se proporcionaba un mecanismo de autentificación básico, basado en cabeceras de autentificación para solicitar datos del usuario (el servidor) y para enviar los datos del usuario (el cliente). Esta autentificación no proporciona confidencialidad ni integridad, sólo se emplea una codificación Base64.
2. Autentificación digest
También vimos que existía una variante de lo anterior, la autentificación digest, donde, en lugar de transmitir el password por la red, se emplea un password codificado utilizando el método de encriptado MD5. Sin embargo, algunos servidores no soportan este tipo de autentificación.
3. Autentificación basada en formularios
Con este tipo de autentificación, el usuario introduce su login y password mediante un formulario HTML (y no con un cuadro de diálogo, como las anteriores). El fichero descriptor contiene para ello entradas que indican la página con el formulario de autentificación y una página de error. Tiene el mismo inconveniente que la autentificación basic: el password se codifica con un mecanismo muy pobre.
4. Certificados digitales y SSL
También con HTTP se vio el mecanismo de funcionamiento de SSL y los certificados digitales, apoyados en los sistemas de criptografía de clave pública. Así, la capa SSL, trabajando entre TCP/IP y HTTP, aseguraba, mediante criptografía de clave pública, la integridad, confidencialidad y autentificación.
En este apartado se explican algunas técnicas comunes empleadas en la seguridad declarativa, como son la autentificación basada en formularios y la autentificación basic.
Veremos ahora con más profundidad la autentificación basada en formularios comentada anteriormente. Esta es la forma más comúnmente usada para imponer seguridad en una aplicación, puesto que se emplean formularios HTML.
El programador emplea el descriptor de despliegue para identificar los recursos a proteger, e indicar la página con el formulario a mostrar, y la página con el error a mostrar en caso de autentificación incorrecta. Así, un usuario que intente acceder a la parte restringida es redirigido automáticamente a la página del formulario, si no ha sido autentificado previamente. Si se autentifica correctamente accede al recurso, y si no se le muestra la página de error. Todo este proceso lo controla el servidor automáticamente.
Este tipo de autentificación no se garantiza que funcione cuando se emplea reescritura de URLs en el seguimiento de sesiones. También podemos incorporar SSL a este proceso, de forma que no se vea modificado el funcionamiento aparente del mismo.
Para utilizar la autentificación basada en formularios, se siguen los pasos que veremos a continuación. Sólo el primero es dependiente del servidor que se utilice.
1. Establecer los logins, passwords y roles
Aquí se establece una lista de usuarios, con su password y uno o varios roles a los que pueden pertenecer.
Tomcat permite especificar la forma de gestionar estos datos (mediante base de datos, fichero de passwords, etc). Sin embargo, también propone una forma alternativa de tratar esta información, almacenando logins, passwords y roles en el fichero conf/tomcat-users.xml.
Este fichero contiene una cabecera XML, seguida de una etiqueta raíz <tomcat-users>, que a su vez contiene una serie de etiquetas <user>, una por cada usuario que se defina. Cada una de estas etiquetas <user> contiene 3 atributos:
Un ejemplo de fichero sería:
<?xml version="1.0" encoding="ISO-8859-1" ?> <tomcat-users> <user name="pepe" password="pepepw" roles="usuario"/> <user name="manuel" password="manolo" roles="admin"/> <user name="toni" password="toni" roles="usuario, admin"/> </tomcat-users>
Así, por ejemplo, para un recurso (URL) al que sólo puedan acceder roles de tipo admin, podrían acceder los usuarios manuel y toni. Notar también que los passwords están visibles en un fichero de texto fácilmente accesible por casi cualquiera, con lo que no es una buena forma de gestionar los passwords para una aplicación profesional.
2. Indicar al servlet que se empleará autentificación basada en formularios, e indicar las páginas de formulario y error.
Se coloca para ello una etiqueta <login-config> en el descriptor de despliegue. Dentro, se emplean las subetiquetas:
Por ejemplo, podemos tener las siguientes líneas en el descriptor de despliegue:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> ... <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page> /login.jsp </form-login-page> <form-error-page> /error.html </form-error-page> </form-login-config> </login-config> ... </web-app>
El formulario de esta página debe contener campos para introducir el login y el password, que deben llamarse j_username y j_password. La acción del formulario debe ser j_security_check, y el METHOD = POST (para no mostrar los datos de identificación en la barra del explorador). Por ejemplo, podríamos tener la página:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <body> <form action="j_security_check" METHOD="POST"> <table> <tr><td> Login: <input type="text" name="j_username"> </td></tr> <tr><td> Password: <input type="text" name="j_password"> </td></tr> <tr><td> <input type="submit" value="Enviar"> </td></tr> </table> </form> </body> </html>
La página puede tener el mensaje de error que se quiera. Ante fallos de autentificación, se redirigirá a esta página con un código 401. Un ejemplo de página sería:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <body> <h1>ERROR AL AUTENTIFICAR USUARIO</h1> </body> </html>
5. Indicar qué direcciones deben protegerse con autentificación
Para ello utilizamos etiquetas <security-constraint> en el descriptor de despliegue. Dichos elementos debe ir inmediatamente antes de <login-config>, y utilizan las subetiquetas:
NOTA: este modo de restricción se aplica sólo cuando se accede al recurso directamente, no a través de arquitecturas MVC (Modelo-Vista-Controlador), con un RequestDispatcher. Es decir, si por ejemplo un servlet accede a una página JSP protegida, este mecanismo no tiene efecto, pero sí cuando se intenta a acceder a la página JSP directamente.
En teoría esta etiqueta es opcional, pero omitiéndola indicamos que ningún rol tiene permiso de acceso. Aunque esto puede parecer absurdo, recordar que este sistema sólo se aplica al acceso directo a las URLs (no a través de u modelo MVC), con lo que puede tener su utilidad.
Añadimos alguna dirección protegida al fichero que vamos construyendo:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <security-constraint> <web-resource-collection> <web-resource-name> Prueba </web-resource-name> <url-pattern> /prueba/* </url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> <role-name>subadmin</role-name> </auth-constraint> </security-constraint> <login-config> ... </web-app>
En este caso protegemos todas las URLs de la forma http//host/ruta_aplicacion/prueba/*, de forma que sólo los usuarios que tengan roles de admin o de subadmin podrán acceder a ellas.
6. Desactivar los servlets
Hay que tener en cuenta que al proteger URLs, seguimos dejando libre una vía de acceso para los servlets, puesto que podemos llamarlos utilizando el alias servlet. Por ejemplo, supongamos que mapeamos el servlet paqueteservlets.UnServlet con la dirección /pruebas/Prueba. Supongamos también que protegemos la url /pruebas/Prueba. De esta forma, si llamamos al servlet con
http://<ruta>/pruebas/Prueba
no podremos hacerlo a no ser que tengamos los permisos adecuados. Pero nadie ha prohibido llamar al servlet con:
http://<ruta>/servlet/paqueteservlets.UnServlet
Hay que desactivar, por tanto, esta forma de llamar a los servlets implicados en el proceso de seguridad. En los conceptos iniciales de servlets vistos anteriormente vimos cómo podría hacerse eso. Una forma muy común es redirigir las direcciones /servlet/* hacia un mismo servlet que muestre una página de error:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <servlet> <servlet-name>Error</servlet-name> <servlet-class>ErrorServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Error</servlet-name> <url-pattern>/servlet/*</url-pattern> </servlet-mapping> <security-constraint> ... </web-app>
El servlet ErrorServlet puede simplemente mostrar un texto de error.
EJEMPLO
Juntando todos los pasos previos, tenemos aquí un fichero WAR disponible con los ficheros implicados. El ejemplo tiene la siguiente estructura de directorios y archivos:
Para probar el ejemplo, debemos colocar en el fichero tomcat-users.xml los valores de usuarios y passwords vistos antes en el ejemplo (u otros valores, si queremos). Se proporciona un fichero tomcat-users.xml en el WAR, para copiarlo directamente en el directorio conf de Tomcat (deberíamos copiar previamente el tomcat-users.xml original, para no perderlo). Luego copiamos el fichero WAR en el directorio webapps de Tomcat. Intentamos acceder a las direcciones:
http://localhost:8080/ejemploseguridadform/prueba/index.html
http://localhost:8080/ejemploseguridadform/servlet/PruebaServlet
En el primero caso, probamos a introducir logins y passwords válidos e inválidos, para comprobar qué páginas se muestran en cada caso. En el segundo caso, nos mostrará la página que genera el servlet ErrorServlet.
El método de autentificación basada en formularios tiene algunos inconvenientes: si el navegador no soporta cookies, el proceso tiene que hacerse mediante reescritura de URLs, con lo que no se garantiza el funcionamiento.
Por ello, una alternativa es utilizar el modelo de autentificación basic de HTTP, donde se emplea un cuadro de diálogo para que el usuario introduzca su login y password, y se emplea la cabecera Authorization de petición para recordar qué usuarios han sido autorizados y cuáles no. Una diferencia con respecto al método anterior es que es difícil entrar como un usuario distinto una vez que hemos entrado como un determinado usuario (habría que cerrar el navegador y volverlo a abrir).
Al igual que en el caso anterior, podemos utilizar SSL sin ver modificado el resto del esquema del proceso.
El método de autentificación basic consta de los siguientes pasos:
1. Establecer los logins, passwords y roles
Este paso es exactamente igual que el visto para la autentificación basada en formularios.
2. Indicar al servlet que se empleará autentificación BASIC, y designar los dominios
Se utiliza la misma etiqueta <login-config> vista antes, pero ahora una etiqueta <auth-method> con valor BASIC. Se emplea una subetiqueta <realm-name> para indicar qué dominio se empleará en la autorización. Por ejemplo:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> ... <login-config> <auth-method>BASIC</auth-method> <realm-name>dominio</realm-name> </login-config> ... </web-app>
3. Indicar qué direcciones deben protegerse con autentificación
Este paso también es idéntico al visto en la autentificación basada en formularios.
4. Desactivar los servlets
Este paso también es igual al visto en la autentificación basada en formularios.
EJEMPLO
Juntando todos los pasos previos, tenemos aquí un fichero WAR disponible con los ficheros implicados. El ejemplo tiene la misma estructura de directorios que el visto para autentificación con formularios (quitando las páginas de login y error, que ya no son necesarias). El fichero descriptor queda ahora con esta apariencia.
Para probar el ejemplo, seguimos también los mismos pasos que para el ejemplo anterior.
Podemos incorporar SSL a los métodos de autentificación basados en seguridad declarativa vistos antes, sin más que incluyendo, dentro de la etiqueta <security-constraint>, una subetiqueta <user-data-constraint>. No necesitamos modificar el resto de la estructura para utilizar SSL, lo único que se necesita es adaptar el servidor Web para que soporte SSL, y añadir esta etiqueta, que contiene:
La API de servlets proporciona, además, una forma de requerir a los clientes que se identifiquen con un certificado. Se puede proporcionar un valor de CLIENT-CERT a la etiqueta <auth-method> de <login-config>. Sin embargo, sólo los servidores que soporten J2EE por completo soportarán esta posibilidad. Incluso los servidores que soportan las versiones 2.3 de servlets y 1.2 de JSP no tienen por qué soportar SSL, con lo que esta característica puede no ser portable.
EJEMPLO
Utilizamos el mismo ejemplo visto en la autentificación BASIC, añadiendo un directorio /ssl, con un fichero index.html dentro. Luego, en el fichero descriptor protegemos dicho directorio con SSL, quedando:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <security-constraint> <web-resource-collection> <web-resource-name> Prueba </web-resource-name> <url-pattern> /prueba/* </url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> <role-name>subadmin</role-name> </auth-constraint> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name> PruebaSSL </web-resource-name> <url-pattern> /ssl/* </url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> <role-name>subadmin</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee> CONFIDENTIAL </transport-guarantee> </user-data-constraint> </security-constraint> <login-config> ... </web-app>
Dejamos protegido el recurso Prueba del ejemplo anterior de la misma forma que está, y añadimos PruebaSSL, para proteger el directorio /ssl/* con SSL.
Aquí está el fichero WAR con toda la aplicación, y aquí el fichero descriptor. Para probarlo, podemos acceder a las direcciones:
http://localhost:8080/ejemploseguridadform/prueba/index.html
http://localhost:8080/ejemploseguridadform/servlet/PruebaServlet
http://localhost:8080/ejemploseguridadssl/ssl/index.html
En los dos primeros casos, el comportamiento debe ser el mismo que en los ejemplos anteriores. En el tercer caso, entrará en juego la configuración SSL y nos mostrará pantallas para aceptar certificados, redirigiendo la petición a https.
La seguridad declarativa vista hasta ahora es la más comúnmente usada en las aplicaciones Web. Pero, ¿cómo hacer que nuestros servlets sean independientes de determinadas configuraciones del servidor, como la gestión de logins y passwords de usuarios, o la gestión de roles? Aquí entra en juego la seguridad programada, y con ella, dejamos que los servlets y páginas JSP controlen parcial o totalmente los aspectos de seguridad.
En algunos casos puede interesar mantener parte de las ventajas que ofrece la seguridad declarativa (como la gestión automática de usuarios y passwords), y añadir sobre eso las configuraciones particulares que podemos alcanzar con la seguridad programada. Así, no tenemos que renunciar a los elementos <login-config>, la autentificación basic o la basada en formularios, los elementos <security-constraint>, etc. Simplemente añadimos un tratamiento adicional a estas autentificaciones.
Para poder combinar los dos tipos de seguridad, tenemos algunos métodos de utilidad en la clase HttpServletRequest:
String getRemoteUser() boolean isUserInRole(String role) String getAuthType()
Por ejemplo, supongamos que en el fichero de usuarios tenemos un usuario con login manuel, que tiene como rol admin (uno de los usuarios de un fichero de passwords visto antes). Sin embargo, queremos utilizar un servlet MiServlet que utiliza un rol supervisor en lugar de admin. Lo que tenemos que hacer es indicar con las etiquetas vistas antes que el rol admin se mapea en supervisor para este servlet:
<?xml...?> <!DOCTYPE...> <web-app> ... <servlet> <servlet-name> Prueba </servlet-name> <servlet-class> MiServlet </servlet-class> <security-role-ref> <role-name> supervisor </role-name> <role-link> admin </role-link> </security-role-ref> </servlet> ... </web-app>
Podemos utilizar dentro del servlet tantos elementos <security-role-ref> como sean necesarios para mapear todos los roles que se quiera.
Con getAuthType() se obtiene el tipo de autentificación empleada.
EJEMPLO
Vemos el siguiente ejemplo, empaquetado en este fichero WAR, y con la siguiente estructura:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc. //DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <servlet> <servlet-name> Programada </servlet-name> <servlet-class> SeguridadProgramadaServlet </servlet-class> <security-role-ref> <role-name> supervisor </role-name> <role-link> admin </role-link> </security-role-ref> </servlet> <servlet-mapping> <servlet-name> Programada </servlet-name> <url-pattern> /ejemploProgramada </url-pattern> </servlet-mapping> <security-constraint> <web-resource-collection> <web-resource-name> Protegido </web-resource-name> <url-pattern> /ejemploProgramada/* </url-pattern> </web-resource-collection> <auth-constraint> <role-name>*</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>Dominio</realm-name> </login-config> </web-app>
donde se mapea el servlet a la dirección /ejemploProgramada/, y se mapea el rol admin con un alias supervisor que se puede utilizar en su lugar en el código del servlet. Finalmente, se protege la ruta /ejemploProgramada/* con autentificación BASIC.
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class SeguridadProgramadaServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out=response.getWriter(); out.println ("<HTML><BODY>"); out.println ("<h2>Usuario 'admin':" + (request.isUserInRole("admin")? "SI":"NO") + "</h2>"); out.println ("<h2>Usuario 'supervisor':" + (request.isUserInRole("supervisor")? "SI":"NO") + "</h2>"); out.println ("<h2>Usuario 'usuario':" + (request.isUserInRole("usuario")? "SI":"NO") + "</h2>"); out.println ("</BODY></HTML>"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
Para probar el ejemplo, intentamos acceder al servlet con:
http://localhost:8080/ejemploseguridadcombinada/ejemploProgramada
Si entramos con alguno de los usuarios del fichero de passwords visto en ejemplos anteriores, nos dirá los roles que tiene dicho usuario.
Como hemos visto, dejar en parte o totalmente que el contenedor gestione la seguridad tiene como inconvenientes que cuestiones como la gestión de usuarios son dependientes del servidor. Por ello en algunos casos interesa dejar que el servlet o la página JSP controle dicha gestión de usuarios también, convirtiendo la seguridad en algo completamente programado.
Para implementar una seguridad completamente programada utilizando un método basic (el método digest no lo veremos por no estar demasiado difundido), los pasos son:
1. Comprobar si hay una cabecera "Authorization"
Si no la hay, vamos al paso 5
2. Obtener el login y password codificados de dicha cabecera
Si hay cabecera Authorization tendrá el siguiente formato:
Authorization: Basic datos_codificados
Saltándonos los 6 primeros caracteres ("Basic ") obtendremos los datos codificados que deberemos descodificar.
3. Descodificar el login y password con Base64
Se utiliza para ello un objeto Base64Decoder (que viene con JDK, en el paquete sun.misc), y llamando a su método decodeBuffer(), que devuelve una cadena con el formato:
usuario:password
Podemos luego separar login y password procesando esta cadena. Hay que tener en cuenta también que, al estar en el paquete sun.misc, no se garantiza la portabilidad del descodificador entre sistemas distintos.
4. Comprobar si login y password son correctos
Para ello se pueden tener los login y passwords guardados en una base de datos, o en un objeto Properties en el servlet, o cualquier otra posibilidad. Se trata de buscar el password que hay en la base de datos para el usuario que ha entrado y compararlo con el que ha introducido.
5. Si no hay autentificación, o falla, enviar la respuesta apropiada
Se envía un código 401, y una cabecera WWW-Authenticate para que el navegador muestre un diálogo y que el usuario se autentifique.
EJEMPLO
Como ejemplo de seguridad completamente programada se tiene el ejemplo de cabeceras de autentificación visto en el apartado de cabeceras y códigos.
Al igual que con la seguridad declarativa, podemos incorporar SSL en nuestra aplicación Web con seguridad programada, aunque se requiere algo más de trabajo para poderlo incorporar. Además de adaptar el servidor Web para que permita SSL, entre otras cosas, se puede requerir:
Determinar si se está usando SSL
La interfaz ServletRequest tiene los métodos:
String getScheme() boolean isSecure()
El primero devuelve "http" para peticiones normales y "https" para peticiones SSL. El segundo devuelve true para peticiones SSL, y false para peticiones normales.
Redirigir peticiones que no sean SSL
Con la seguridad declarativa utilizábamos las etiquetas <user-data-constraint> y subetiquetas <transport-guarantee> para indicar que se redirigieran las peticiones http a https.
Con seguridad programada, tenemos que hacer esta redirección a mano, empleando el método sendRedirect() de ServletResponse. El problema está en que la conversión de la URL desde http a https debe hacerse a mano, obteniendo la cadena de la URL con el método getRequestURL() de ServletRequest, cambiando luego http por https en la cadena y enviando esa cadena procesada al sendRedirect() (enviando también los datos del formulario tomándolos con getQueryString())
Obtener el número de bits de la clave y algoritmo
A partir de la versión 2.3 de servlets, podemos acceder a un atributo llamado:
"javax.servlet.request.key_size"
pasándole este nombre al método getAttribute() del objeto HttpServletRequest. Se devuelve un objeto con el número de bits de la clave de encriptado, que luego tendremos que convertir a Integer para obtener su valor.
De forma parecida podemos obtener el tipo de algoritmo empleado para encriptar, utilizando para ello el atributo:
"javax.servlet.request.cipher_suite"
que deberemos convertir luego de Object a String.
Acceder a los certificados X509 del cliente
Los certificados X509 están especificados en la RFC 1421. Cuando un cliente se autentifica con este certificado, el valor del mismo se coloca en el atributo
"javax.servlet.request.X509Certificate"
de la petición. Este atributo está accesible tanto en la versión 2.2 como la 2.3 de servlets. Al obtenerlo, tendremos que convertirlo luego de Object a X509Certificate.
Una vez tengamos el certificado, podemos obtener el nombre del usuario, número de serie, valor de la firma, clave pública, etc. Para más información, se puede consultar la dirección:
http://java.sun.com/j2se/1.3/docs/api/java/security/cert/X509Certificate.html
EJEMPLO
El siguiente servlet muestra en una página toda la información sobre la conexión SSL, o bien redirige una petición http a https si es el caso:
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.security.cert.*; public class ServletSSL extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (request.getScheme().equalsIgnoreCase("http")) { // Convertimos la URL a https String urlInicial = request.getRequestURL().toString(); int indice = urlInicial.indexOf(":"); String urlNueva = urlInicial.substring(0, indice) + "s" + urlInicial.substring(indice); String datos = request.getQueryString(); if (datos != null) urlNueva = urlNueva + "?" + datos; response.sendRedirect(urlNueva); } else { // Mostramos los datos de la conexion SSL PrintWriter out = response.getWriter(); out.println ("<HTML>"); out.println ("<BODY>"); if (request.isSecure()) { // Bits de la clave Integer tamClave = (Integer)request.getAttribute( "javax.servlet.request.key_size"); String cadenaTam = (tamClave != null)? "" + tamClave.intValue(): "Desconocido"; out.println ("<LI>Tamaño: " + cadenaTam + "</LI>"); // Algoritmo empleado String algoritmo = (String)request.getAttribute( "javax.servlet.request.cipher_suite"); String cadenaAlg = (algoritmo != null)? algoritmo: "Desconocido"; out.println ("<LI>Algoritmo: " + cadenaAlg + "</LI>"); // Datos del certificado X509Certificate cert = (X509Certificate)request.getAttribute( "javax.servlet.request.X509Certificate"); String cadenaCer = (cert != null)? cert.toString(): "Desconocido"; out.println ("<LI>Certificado: " + cadenaCer + "</LI>"); } out.println ("</BODY>"); out.println ("</HTML>"); } } }