Category Archives: Seguridad

Encriptación y desencriptación con Java

Este post está dedicado al uso de la arquitectura estándar de criptografía de Java (JCA). El objetivo del post es mostrar diferentes casos de uso en los que se aplican, desde las APIs de JCA, diversas utilidades relacionadas con soluciones criptográficas estándar, como son:

  • Encriptacion y desencriptación usando diferentes algoritmos.
  • Uso de almacenes de claves.
  • Generación de claves.
  • Encriptación y desencriptación de ficheros.
  • Algoritmos de ofuscación de datos (digest).
  • Uso de proveedores de algoritmos.

Los ejemplos que se presentan a continuación son parte de un proyecto completo desarrollado como un ejercicio práctico de aplicación de los diferentes mecanismos antes mencionados. El proyecto completo se puede descargar desde el siguiente enlace.

1. Encriptación y desencriptación de texto plano.

El texto a encriptar o desencriptar, una vez encriptado o desencriptado, se almacena como un array de bytes.

Esta línea nos permite obtener la representación del algoritmo de encriptación o desencriptación a usar. Veremos más adelante como se implementa.

Dependiendo del modo establecido, se encripta o desencripta.

  • Al encriptar, se obtienen los bytes del texto a encriptar. Se generan los bytes de salida del texto encriptado. Después, para presentarlo o almacenarlo como texto, se codifican, por ejemplo en BASE64.
  • Al desencriptar, primero se decodifica el BASE64 para obtener el texto encriptado como array de bytes, estos se desencriptan y se traducen como texto desencriptado final.

Para obtener la representación del algoritmo de encriptación/desencriptación a usar (lo que haría la funcion ‘getCipher‘):

Se instancia el algoritmo con el nombre del mismo tal y como figura en el proveedor seleccionado (‘Blowfish’, ‘DES’, ‘AES’, ‘DESSede’, ‘RSA’, etc.).

Se genera una clave secreta con el password a utilizar (si el algoritmo es simétrico, es decir, que solo usa una clave) y se inicializa el mismo.

Si se usa un algoritmo asimétrico (es decir, que usa un par de claves, públic/privada para encriptar/desencriptar) como por ejemplo el RSA, entonces tenemos lo siguiente.

Generamos el par de claves (más adelante veremos como leerlo de un almacen de claves).

Elegimos la clave a usar, bien para encriptar o bien para desencriptar. En este caso usamos la clave pública para encriptar y la privada para desencriptar.

2. Uso del almacén de claves.

El almacen de claves es un mecanismo clásico de Java para almacenar pares de claves y generar certificados de autenticación. Estas claves pueden ser usadas para encriptar y desencriptar como se ha visto anteriormente. La ventaja del almacen de claves es que éstas estarían protegidas en el mismo de forma segura (bajo password) y el almacen de claves puede ser facilmente transportado o compartido en caso de ser necesario. Además varios mecanismos de seguridad en Java usan los almacenes de claves para establecer canales de comunicación seguros, como puede ser en el caso de conexiones SSL.

Para poder acceder al almacen de claves desde la API de JCA basta con realizar lo siguiente. Si se usa un tipo de almacen de claves distinto al creado por defecto (PKCS, etc.) entonces se puede especificar uno diferente:

Para obtener la clave privada y o la pública de uno de los pares de claves almacenados se invocan las siguientes funciones. Se indica el nombre del par de claves usado al crear el mismo, es decir, el alias que se uso al crearlo.

3. Generación de claves.

La API de JCA también permite generar claves de forma dinámica. Anteriormente se ha mostrado como generar un par de claves de forma dinmámica para usarlo con algoritmos asimétricos. También se pueden generar claves individuales para los algoritmos simétricos de la siguiente manera:

Para aquellos algoritmos que usan “Password Base Encription” o PBE, la cosa sería como sigue:

4. Encriptación y desencriptación de ficheros.

La encriptación y desencriptación de ficheros trata el procesamiento del contenido de los mismos. JCA ofrece unas APIs que facilitan la labor adicional de procesar este contenido y almacenar el nuevo contenido generado, bien encriptado o desencriptado.

Los pasos comunes a la encriptación y desencriptación de texto plano sería la inicialización del algoritmo de cifrado, tal y como se ha visto anteriormente.

Se usa un buffer para ir leyendo del fichero de entrada de a pocos:

Se instancia la clase que se encargará de encriptar o desencriptar, dependiendo del modo en el que se haya inicializado el algoritmo y al mismo tiempo escribirá la salida en el fichero indicado:

Por otro lado, se lee del fichero de entrada en el buffer, y se pasa el buffer al objeto anterior para que se encargue de encriptarlo o desencriptarlo y escribirlo al fichero de salida al mismo tiempo:

 ¡Y ya estaría hecho! Tan solo hay que tener en cuenta que los algoritmos de encriptación asimétricos, los que usan pares de claves, no son aptos para estos menesteres, ya que los bloques de datos que pueden encriptar y desencriptar están limitados por el tamaño de la clave. Por ello lo normal es usar para encriptar ficheros algoritmos simétricos de una sola clave, que no tienen esta limitación, y por ejemplo usar los algoritmos asimétricos, más robustos, para encriptar la clave usada en la encriptación del fichero.

5. Algoritmos de ofuscación de datos (digest).

Los algoritmos de “digestión” de mensajes de texto son mecanismos de ofuscación de información que también pueden ser usados como validadores de integridad de los datos o el texto que se ofusca, ya que el resultado sobre estos debe ser siempre el mismo. Así la salida que se obtiene ha de ser igual, o de lo contrario esto indicará una diferencia en los datos de origen. Entre estos algoritmos se encuentran los “Message Digest” o las funciones “Hash“, por ejemplo. Los primeros son fáciles de usar desde la API de JCA tal que asi:

 Los datos de salida se pueden representar como texto en alguna codificación concreta como puede ser BASE64 o hexadecimal.

6. Uso de proveedores de algoritmos.

Se pueden visualizar facilmente con que proveedores de algoritmos cuenta nuestro JDK, asi como los algoritmos implementados por el mismo. También es posible incorporar a nuestra aplicación diferentes proveedores de algoritmos de encriptación, que además si usan la misma API de JCA, se integrarán perfectamente con el resto de los existentes.

Para obtener la lista de los nombres de los proveedores existentes se realiza la siguiente operación:

La lista de los algorimos de cifrado, dado un proveedor se obtendría tal que así:

La de los algoritmos de “digestión”, de forma similar pero cambiando la etiqueta de lo que se busca:

Por último, para agregar nuevos proveedores a nuestra aplicación, basta con invocar la siguiente API:

Pasando la instancia del proveedor (o en este caso el nombre de la clase del proveedor), una vez las librerias del mismos se encuentran en el classpath a disposición del aplicativo, sería suficiente. BouncyCastle es uno de los proveedores clásicos de algoritmos para JCA más famosos.

Se puede descargar el proyecto completo (Jcrypto) para Eclipse desde este enlace. La libreria de BC usada esta aquí.

NOTA

Se puede obtener la versión usada (u otra) de la librería de Bouncy Castle mediante una dependencia de Maven (está disponible en el repositorio de mvnrepository). La dependencia usada en este caso es:

Además, se ha subido el código, pom.xml y el proyecto de Eclipse entero al repositorio enlazado de Github, para mejor gestión y mayor facilidad a la hora de compartir. Se aconseja usar esta fuente como recurso para obtener el proyecto y los ejemplos descritos, los cuales se han revisado y se han incluido ligeras mejoras.

Breve ejemplo con Spring Security

Este es un sencillo ejemplo del uso del mecanismo de autorización y autenticación para aplicaciones de Spring, conocido como Spring Security, empleado aquí para autorizar el acceso de usuarios a una aplicación web.

En este caso, el planteamiento es el siguiente:

- Una aplicación web.

- Qué cada vez que se acceda a cualquier parte de la aplicación, se chequee si el usuario en cuestión esta autorizado para acceder.

- Un formulario de “login” para autenticarse por primera vez.

- Una página de bienvenida, para la primera vez que se autoriza.

En el ejemplo se usará:

- Un filtro web de Spring para redireccionar las peticiones al mecanismo de autorización de Spring.

- Un configuración de autorización básica via HTTP.

- Un formulario de “login”.

- Un “datasource” que de acceso a una base de datos, donde se encuentran registrados los usuarios y los grupos.

- Un gestor de autenticación de Spring que haga uso de este “datasource” y de unas queries SQL concretas para obtener los datos de autorización del usuario logado.

1. Añadimos las dependencias correspondientes a nuestro proyecto o descargamos los JARs asociados:

 

2. Creamos el modelo en base de datos que contiene la información de los usuarios y grupos que serán autorizados. Se necesitan dos tablas, una para los usuarios y otra para los roles o grupos. La tabla de usuarios (USERS) ha de tener al menos los siguientes campos:

USERNAME PASSWORD ENABLED
pepe perez 1

Y la tabla de roles (ROLES) puede ser:

ROLENAME USERNAME
ROLE_USER pepe

3. Habilitamos el filtro de Spring en el web.xml de nuestro proyecto web:

 

4. Creamos el fichero de configuración de Spring Security:

 

Si no tenemos “Datasource” declarado, y estamos usando Tomcat como servidor de aplicaciones, podemos incluir uno en el fichero “META-INF/context.xml” de nuestra aplicación. Una definición clásica de un “Datasource” usando este mecanismo sería:

 

5. Se incluye el fichero de configuración anterior en el contexto de la aplicación. Se importa en el “applicationContext.xml” de la aplicación. Si el fichero se llama “spring-security.xml” y se encuentra dentro del directorio “WEB-INF/spring”, estando el “applicationContext.xml” en “WEB-INF”, entonces sería:

 

6. La página de login, con el formulario de Spring Security, tendría este aspecto:

 

7. Finalmente, la página de bienvenida podría hacer uso de las etiquetas de Spring Security para mostrar el nombre del usuario logado. También se puede incluir aquí un enlace para hacer un “logout”:

 

Al lanzar la aplicación, se puede ver algo asi:

SpringSecurityLogin

Formulario “Login” de Spring Security

welcomePage

Página de bienvenida con el nombre del usuario logado y el enlace de “Logout”

Autenticación mútua basada en certificados

El uso de certificados es un método extendido dentro del mundo de la web y las aplicaciones cliente/servidor para crear conexiones seguras vía SSL y para autenticar/autentificar quién está detras de cada extremo de la conexión.

Un aplicación de los certificados es su uso para realizar una autenticación mútua entre un cliente y un servidor. Esto permite:

- Establecer una conexión segura ya que al contar el lado servidor con un par de claves, que será el origen de su certificado, puede también establecer una conexión segura vía SSL.

- Autenticar, no solo el lado servidor de la conexión, es decir, a que se está conectando el cliente, si no también autenticar al cliente, desde el punto de vista del servidor. Esto aumentará la seguridad del lado servidor, permitiendo el acceso solo a aquellos cliente que cuenten con el certificado deseado.

Los pasos para implementar una autenticación mútua con certificados usando el servidor de aplicaciones Tomcat y la herramienta de generación de claves y certificados de Java keytool, presente en cualquier JDK, son los siguientes:

1. Si aún no se tiene, se crea el almacén de claves del servidor con su par de claves correspondiente:

 

2. Se exporta el certificado del servidor a partir del par de claves anteriormente creado:

 

3. Se añade el certificado del servidor al almacén de claves que usa el cliente desde la aplicación que emplea para conectar al servidor:

 

4.  Creamos un nuevo par de claves para el cliente:

Almacenamos el par de claves como de tipo PKCS12 para poder importar posteriormente otros certificados de cliente generados a partir de los navegadores web.

5. Generamos el certificado del cliente:

 

6. Añadimos al almacén de claves del servidor el certificado del cliente que queremos autenticar:

 

7. Si aún no se ha relizado, se configura Tomcat para que use la conexión segura vía SSL:

En el fichero de configuración de Tomcat <TOMCAT_HOME>\conf\server.xml se descomenta el conector definido para SSL. Además se añaden la ruta al almacén de claves del servidor y el password para acceder al mismo.

Se reinicia Tomcat.

8. Se ha de crear el usuario y rol asociados al cliente a autenticar. Esto se puede realizar de diferentes maneras y usando distintos mecanismos de autenticación y autorización de usuarios con los que cuenta Tomcat. El más sencillo de todos es el uso del fichero de usuarios de Tomcat. Si se opta por este mecanismo, los pasos a seguir son los siguientes:

Editar en el fichero de usuarios de Tomcat (<TOMCAT_HOME\conf\>tomcat-users.xml) y añadir al mismo el rol y el usuario autenticado con el certificado anteriormente importado. Por ejemplo:

La cadena incluida como username ha de ser exactamente el conjunto de valores que se usaron para crear el par de claves del cliente. Reiniciar Tomcat para que los cambios tengan efecto.

9. Por último, en el descriptor de la aplicación web del lado servidor se ha de configurar la restricción de seguridad que dara pie a la autenticación mediante certificados. Para ello, se edita el fichero WEB-INF/web.xml y se incluye, dentro de la etiqueta principal de web-app lo siguiente:

Mediante la configuración anterior se indica lo siguiente:

<url-pattern>/MainServlet</url-pattern> – indica el contexto dentro de la aplicación web que se verá afectada por la restricción.

<http-method/> – se especifica que métodos HTTP se autenticarán.

<role-name>cliente</role-name> – es el nombre del rol al que se le permite el acceso.

<transport-guarantee>CONFIDENTIAL</transport-guarantee> – indica que el acceso ha de ser vía HTTPS de forma obligatoria.

<auth-method>CLIENT-CERT</auth-method> – activa el método de autenticación por certificado.

10. Las pruebas:

Las pruebas de autenticación pueden ser realizadas bien desde un navegador web que intente acceder a la URL servidora protegida o bien con una aplicación de cliente, como un cliente HTTP(S).

- Navegador Web

Sería necesario importar el certificado del cliente al mismo. Una vez hecho esto, basta con intentar acceder a la URL que se quiere testear. Si la autenticación es validad, se accederá sin problemas, si no el navegador indicará el error.

- Cliente HTTP(S)

Ha de importar ámbos certificados, el de cliente y el de servidor, en el almacén de claves que use la aplicación. Si la aplicación está implementada en Java, se puede usar el almacén de claves por defecto de la JVM, que se encuentra en <JAVA_HOME>\ jre\lib\security\cacerts.