{short description of image}

Encriptación

 

Introducción

Criptografía tradicional.

En la criptografía tradicional, (también llamada criptografía de "clave secreta") tanto el emisor como el receptor poseen una misma clave o password. El emisor utiliza esta clave para cifrar el mensaje obteniéndose el "mensaje cifrado", que es ilegible. El receptor utiliza la misma clave utilizada para el cifrado para descifrar el mensaje y obtener así el mensaje original. Si esta clave es conocida únicamente por emisor y receptor, se asegura que el mensaje recibido es el original y que es enviado por la única persona (además del receptor), que tiene la clave.

Este tipo de criptografía acarrea un problema. Imagínese que existe otro emisor que también envía mensajes cifrados al receptor anterior. Si el segundo emisor tiene la misma clave que el primer emisor, en caso de poder interceptar mensajes de éste, podría descifrarlos ya que utilizan la misma clave. Si, por el contrario, utiliza una clave distinta, el receptor deberá tener tantas claves como potenciales emisores, lo que conduce a una gran complejidad en el mantenimiento de claves.

Este tipo de criptografía puede utilizar distintos algoritmos para cifrar los datos.  Los más utilizados son los siguientes:

Criptografía de clave pública.

La idea del uso de criptografía con "clave pública" es bastante sencilla, aunque los algoritmos y los fundamentos matemáticos en los que se basa no lo sean tanto.

En este tipo de cifrado, el emisor y el receptor poseen claves distintas. El emisor utiliza una clave para cifrar el mensaje y el receptor utiliza otra clave para descifrarlo. El receptor comunica la clave de cifrado a todo el mundo (De ahí el término "clave pública") que quiera enviarle mensajes cifrados, pero se reserva para sí la clave capaz de descifrar los mensajes. A esta clave que únicamente conoce el receptor se le denomina "clave privada" y es la única capaz de descifrar los mensajes generados por la clave pública.

En el ejemplo de la figura, los mensajes enviados por Emisor 1 y 2 sólo pueden ser descifrados por Receptor.

Este tipo de criptografía puede utilizar distintos algoritmos para cifrar los datos.  El más utilizado es el RSA "Rivest, Shamir and Adlerman asymmetricCipher algorithm".

Con este cifrado conseguimos privacidad  aunque no  conseguimos autentificar al emisor.

Autentificación.

Firma digital

Una firma digital sirve básicamente para lo mismo que una firma realizada con una pluma o bolígrafo en un escrito, pero la firma digital tiene una ventaja añadida: además de probar que un documento esta escrito por una determinada persona o institución, asegura que el documento no fue alterado después de ser firmado.

El concepto de "firma digital" se basa en la criptografía de clave pública, pero el proceso es el contrario. Quien "firma" el mensaje lo cifra mediante su "clave privada", de forma que puede ser descifrado por todo el mundo, siempre que posean la "clave pública" correspondiente a la clave privada utilizada para firmar el mensaje. Si la clave pública es capaz de descifrar un mensaje sólo puede ser por un motivo, que éste mensaje fuera cifrado por el poseedor de la clave privada, lo que autentifica su identidad (además de asegurar que el mensaje original no ha sido alterado, de lo contrario no podría ser descifrado por la clave pública).

Lo que se consigue con esto NO es cifrar un mensaje para que sólo pueda ser leído por otra persona (puede ser descifrado por todo aquel que posea la clave pública), sino asegurar a quien lo lee, que realmente ha sido enviado por quien posee la clave privada y que el contenido no ha sido alterado.

Huella digital o "fingerprint".

El proceso de cifrar un mensaje con la clave privada puede consumir un tiempo de proceso valioso. Si es indiferente que el mensaje pueda ser leído por cualquier persona, y no sólo aquellos que posean la clave pública, puede obtenerse mediante una función hash un texto mucho más corto que el mensaje original llamado "huella digital" o fingerprint. Si se altera el mensaje original, también varía la huella digital. Lo que se cifra mediante la clave privada no es el mensaje original completo, sino esta "huella digital"; el resultado se añade al documento a transmitir.

Quien lee el mensaje firmado, en primer lugar aplica la función hash al documento, después descifra la "huella digital" cifrada y compara ésta con la obtenida al procesar el documento. si son iguales, el documento no ha sido alterado y, efectivamente, ha sido enviado por quien firma.

De todos los algoritmos de hashing, los más utilizados son los siguientes:

Existe un algoritmo de Clave pública, diseñado solo para firmar documentos, el DSA.

Certificados.

¿Cómo puede una persona que recibe una clave pública saber que esa clave corresponde realmente a quien dice ser y no a otra persona con pretensiones desconocidas?

Dicho de otra forma: ¿Cómo puede una persona suministrar su clave pública a quien la desee? Sólo con enviarla no es suficiente. Alguien con pretensiones desconocidas podría suministrar una clave pública haciéndose pasar por otra persona y descifrar con la clave privada los mensajes codificados con esa clave pública.

Precisamente para eso, sirve un certificado, para obtener la clave pública de otra persona o entidad.

Un certificado es una declaración firmada digitalmente por una entidad en la que se confía (de la que se tiene su clave pública), indicando que la clave pública de otra persona o entidad tiene un determinado valor.

Para conseguir privacidad y autentificación deberemos utilizar el cifrado de  los datos con la clave pública del receptor y firmarlos con nuestra clave privada..

Criptografía en Java.

Introducción.

El soporte que da el JDK a la criptografía se divide en dos grandes bloques, el JCA "Java Cryptography Architecture" y el JCE "Java Cryptography Extension".  La primera parte nos define las bases del soporte criptográfico de Java, y la segunda nos provee de los algoritmos necesarios para poder encriptar y desencriptar datos.

Debido a las leyes de los Estados Unidos, que prohiben exportar software de encriptación de datos, el JCE no viene incluido en el JDK, y existen restricciones para bajarlo de la site de SUN, pero existen paquetes de terceros, desarrollados fuera de los Estados Unidos, que implementan todas las especificaciones del JCE y que no están sujetos a sus restricciones legales, como por ejemplo la librería CRYPTICS.

Arquitectura Criptográfica de Java (JCA).

El JCA, se refiere al marco para acceder y desarrollar la funcionalidad criptográfica de Java. Incluye todo el API relativo a la criptografía ( el paquete java.security y sus subpaquetes), así como una serie de especificaciones a tener en cuenta por los programadores que quieran crear extensiones criptográficas (como la librería CRYPTICS, etc).

La arquitectura criptografica de Java ha sido diseñada teniendo en cuenta los principios de independencia de la implementación e interoperabilidad. La independencia de la implementación se consigue introduciendo el concepto de "Proveedor" (Cryptography Package Provider).

El termino "Proveedor" se refiere a un paquete o grupo de paquetes que que implementan algoritmos criptográficos específicos.

El JDK 1.1 viene con un proveedor por defecto llamado "SUN", el cual incluye una implementación del algoritmo DSA y una implementación de los algoritmos de Hashing MD5 y SHA-1.

En el JDK se pueden instalar uno o mas paquetes proveedores.

Interfaces y clases del JCA.

SecureRandom

La clase SecureRandom es un generador de números pseudo-aleatorio.

Para crear objetos SecureRandom usaremos uno de los siguientes constructores:

Una vez tenemos un objeto SecureRandom, podemos utilizar los siguientes métodos:

Métodos de la clase SecureRandom
setSeed(byte[]) Reinicializa el SecureRandom utilizando la semilla indicada en el array de bytes.
setSeed(long) Reinicializa el SecureRandom utilizando los ocho bytes del long indicado.
next(int) Devuelve un entero conteniendo el número indicado de bits pseudo-aleatorios.

Key

El interface Key es el interface de mas alto nivel de todas las claves y define la funcionalidad compartida por todas las claves.

Tiene tres métodos:
String getAlgorithm() Devuelve el nombre del algoritmo de esta clave o nulo si es desconocido.
byte[] getEncoded() Devuelve la clave codificada o nulo si la clave no soporta codificación.
String getFormat() Devuelve el formato utilizado para codificar la clave o nulo si la clave no puede ser codificada.

PublicKey y PrivateKey

Los interfaces PublicKey y PrivateKey son interfaces sin métodos, que derivan de la interface Key, utilizados solamente para comprobación de tipos.

KeyPair

La clase KeyPair es simplemente un contenedor de un par de claves (una pública y una privada).

Tiene dos métodos:
PrivateKey getPrivate() Devuelve la clave privada.
PublicKey getPublicKey() Devuelve la clave pública.

KeyPairGenerator

La clase KeyPairGenerator se utiliza para crear un par de claves.

Para crear objetos KeyParGenerator usaremos uno de los siguientes métodos:

Una vez tenemos creado un generador de claves podemos utilizar los siguientes métodos:

Métodos de la clase KeyPairGenerator
initialize(int potencia, SecureRandom random) Inicializa el generador de claves para la potencia indicada utilizando el generador de números pseudo-aleatorio indicado.
initialize(int potencia) Inicializa el generador de claves para la potencia indicada utilizando un generador de números pseudo-aleatorio subministrado por el sistema.
KeyPair generateKeyPair() Genera un par de claves.

Identity

La clase Identity se utiliza para manejar identidades.

Para crear identidades usaremos el constructor siguiente:

Una vez tenemos creada una identidad podemos utilizar los siguientes métodos:

Métodos de la clase Identity
setInfo(String info) Asigna una cadena de información a la identidad.
String getInfo() Devuelve la cadena de información de la identidad.
setPublicKey(PublicKey key) Asigna una clave pública a la identidad.
PublicKey getPublicKey() Devuelve la clave pública de la identidad.
String getName() Devuelve el nombre de la identidad.

Signer

La clase Signer deriva de la clase Identity, y se utiliza para manejar identidades capaces de firmar datos.

Para crear "signers" usaremos el método siguiente:

Una vez tenemos creado un signer podemos utilizar los métodos de la clase Identity además de los siguientes métodos:

Métodos de la clase Signer
setKeyPair(KeyPair pair) Asigna un par de claves al "signer".
PrivateKey getPrivateKey() Devuelve la clave privada del "signer".

Signature

La clase Signature se utiliza para crear o verificar firmas.

Para crear objetos Signature usaremos uno de los siguientes métodos:

Una vez tenemos creado un objeto firma podemos utilizar los siguientes métodos:

Métodos de la clase Signature
initSign(PrivateKey) Inicializa el objeto firma para firmar.
initVerify(PublicKey) Inicializa el objeto firma para verificar.
update(byte b) Actualiza los datos a ser firmados o verificados en un byte.
update(byte data[]) Actualiza los datos a ser firmados o verificados utilizando un array de bytes.
update(byte data[],int off,int len) Actualiza los datos a ser firmados o verificados utilizando una parte de un array de bytes que empieza en la posición off y tiene una longitud len.
byte[] sign() Genera una firma.
boolean verify(byte signature[]) Verifica si la firma que se le pasa como parámetro es correcta.

Ejemplo:

El Ejemplo siguiente consta de dos aplicaciones, una cliente que crea dos claves, lee un fichero, calcula la firma, crea un objeto "SignedData", con los datos anteriores y lo manda por la red a un servidor que leera el objeto "SignedData" y verificara si la firma es correcta.

SignedData.java

ServerDSA.java

ClientDSA.java

Extensión criptográfica de Java (JCE).

El JCE extiende el proveedor "SUN" e incluye una implementación de los algoritmos DES, 3DES, los modos ECB y CBC, y el estilo de relleno PKCS#5. Para utilizar otros algoritmos deberemos utilizar otro proveedor que los implemente. (Para utilizar el algoritmo RSA nosotros utilizaremos la libreria CRYPTIX).

Interfaces y clases del JCE.

SecretKey

La interface SecretKey es una interface sin métodos, que deriva de la interface Key, utilizada solamente para comprobación de tipos.

KeyGenerator

La clase KeyGenerator se utiliza para crear una clave secreta.

Para crear objetos KeyGenerator usaremos uno de los siguientes métodos:

Una vez tenemos creado un generador de claves podemos utilizar los siguientes métodos:

Métodos de la clase KeyGenerator
initialize(SecureRandom random) Inicializa el generador de claves utilizando el generador de números pseudo-aleatorio indicado.
SecretKey generateKey() Genera una clave secreta.

Cipher

La clase Cipher se utiliza para cifrar o descifrar datos.

Para crear objetos Cipher usaremos uno de los siguientes métodos:

Una vez tenemos creado un objeto Cipher podemos utilizar los siguientes métodos:

Métodos de la clase Cipher
initDecrypt(Key key) Inicializa el objeto Cipher para descifrar.
initEncrypt(Key key) Inicializa el objeto Cipher para cifrar.
int getState() Devuelve el estado del objeto Cipher, pudiendo ser uno de los siguientes valores UNINITIALIZED, ENCRYPT,  DECRYPT .
byte[] crypt(byte in[]) Encripta o desencripta (dependiendo de la inicialización) el array especificado.
byte[] crypt(byte in[],int off,int len) Encripta o desencripta una parte del array especificado que empieza en la posición off y tiene una longitud len.
int crypt(byte in[], int inOff, int inLen, byte out[], int outOff) Encripta o desencripta una parte del array especificado que empieza en la posición inOff y tiene una longitud inLen, guardando el resultado a partir de la posición outOff del array out..

CipherInputStream

La clase CipherInputStream es un stream de entrada de datos, que puede encriptar o desencriptar los datos que pasan a traves de el.

Para crear objetos CipherInputStream usaremos el siguiente constructor:

Una vez tenemos creado el stream de entrada podemos utilizar los siguientes métodos:

Métodos de la clase CipherInputStream
int read() Lee el byte siguiente encriptado o desencriptado según el estado del objeto Cipher.
int read(byte b[], int off, int len) Lee len, bytes encriptados o desencriptados, guardandolos en b,  comenzando por b[off]. Devuelve el número de bytes leídos.

CipherOutputStream

La clase CipherOutputStream es un stream de salida de datos, que puede encriptar o desencriptar los datos que pasan a traves de el.

Para crear objetos CipherOutputStream usaremos el siguiente constructor:

Una vez tenemos creado el stream de salida podemos utilizar los siguientes métodos:

Métodos de la clase CipherOutputStream
void write() Escribe un byte encriptado o desencriptado según el estado del objeto Cipher.
void write(byte b[], int off, int len) Escribe len, bytes encriptados o desencriptados, de b, comenzando por b[off].
void flush() Realiza el proceso final y la salida de los datos restantes del proceso de encriptación o desencriptación.

Ejemplo:

El Ejemplo siguiente es el mismo que el ejemplo visto en la sección anterior, pero utilizando en este caso los algoritmos RSA y MD5.

SignedData.java

ServerRSA.java

ClientRSA.java

Ejemplo:

El Ejemplo siguiente consta de dos aplicaciones, una cliente que crea una clave, lee un fichero, y lo manda encriptado por la red a un servidor que creará una clave y desencriptara los datos leidos por la red.

ServerDES.java

ClientDES.java

Ejemplo:

El Ejemplo siguiente consta de tres aplicaciones:
Una aplicación para crear las claves del cliente y del servidor y guardarlas en ficheros.
Una aplicación cliente que lee de un fichero sus dos claves y la clave pública del servidor, lee un fichero, calcula la firma y encripta los datos, crea un objeto "DadesSegures", con los datos anteriores y lo manda por la red.
Una aplicación servidor que lee de un fichero sus dos claves y la clave pública del cliente, lee el objeto "DadesSegures" de la red, desencripta los datos, y verifica si la firma es correcta.

CreaClaus.java

DadesSegures.java

ServerSegur.java

ClientSegur.java