Tag Archives: Cifrado

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.