martes, mayo 24, 2011

Encriptacion utilizando el algoritmo AES

En esta ocacion haremos un pequeño ejemplo de una clase que encripte y desencripte mediante el algoritmo AES (Advanced Encryption Standard)
  
1.- Creamos una clase AESKey
Esta clase solamente contendra una variable que sera nuestra key, que nos servira para encriptar y desencriptar


public class AESKey {
 private String encoded;

 public String getEncoded() {
  return encoded;
 }

 public void setEncoded(String encoded) {
  this.encoded = encoded;
 }
}

2.- Primero crearemos nuestra clase encriptacion

public class AESEncriptacion {
 //el resto del codigo ira aqui :)
}

3.- Crearemos nuestro metodo crearKey

private final String ALGORITMO = "AES";//algoritmo (si cambia la imprementacion tambien)
private final int LONGITUD = 128;//longitud de la llave ()
private final String CODIFICACION = "UTF-8";//como se convertira a byte, esto sera mas adelante

/**
Agrego un constructor que recibe como parametros un AESKey
*/
public AESEncriptacion(AESKey aesKey) throws Exception {  
 this.aesKey = aesKey;
}

public AESKey generaKey() throws Exception {
 KeyGenerator kgen = KeyGenerator.getInstance(ALGORITMO);
 kgen.init(LONGITUD);
 SecretKey skey = kgen.generateKey(); 
 AESKey aesKey = new AESKey();
 aesKey.setEncoded(StringtoHex(skey.getEncoded()));
 return aesKey;
}

4.- Crearemos nuestro metodo Encriptacion

/**
Estoy delegando las exepciones a el metodo que ocupe a encripta.

Convierto una cadena, q en este caso es mi key a un arreglo de bytes
el cual se lo asigno junto con el algoritmo a la clase SecretKeySpec.

Obtengo una instancia de Chiper en base al algoritmo, esta me ayudara
a encriptar y desencriptar mis codigos

A mi variable chiper la pongo en modo encripcion y le paso mi key secreta

Obtengo un arreglo de bytes que representa a mi cadena encriptada (resultado
de la encripcion)

Para que pueda guardar por ejemplo en bd mi cadena encriptada, paso el arreglo
de bytes a una cadena, esto lo hago con ayuda del metodo hexToString que veremos
mas adelante
*/
public String encripta(String cadena) 
  throws NoSuchAlgorithmException,
  NoSuchPaddingException, InvalidKeyException,
  IllegalBlockSizeException, BadPaddingException,
  UnsupportedEncodingException 
{  
  byte[] raw = StringToHex(aesKey.getEncoded());
  SecretKeySpec skeySpec = new SecretKeySpec(raw, ALGORITMO);
  Cipher cipher = Cipher.getInstance(ALGORITMO);
  cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
  byte[] encrypted = cipher.doFinal(cadena.getBytes(CODIFICACION));
  String encriptado = HexToString(encrypted);
  return encriptado;
}

5.- Crearemos nuestro metodo HexToString

/**
Por medio de un for recorro cada posicion del arreglo de bytes

En la variable aux obtengo la posicion deseada, indicando que es un hexadecimal
por medio de 0xff

Como un hexadecimal lo representare con 2 poficiones ff, 0f, 12... etc
en caso de que el resultado fuera menor a 16 solo obtendria 1 posicion
lo que me daria problemas al momento de desencriptar

En caso de ser menor a 16, le concateno un 0 a mi cadena, luego simplemente 
concateno mi variable aux, indicando que la guardate en un formato String
que representara un hexadecimal > Integer.toHexString(aux)

Al Final retorno una cadena que representa a mi arreglo de bytes, de esta forma
podre guardar la informacion en una bd, o imprimirla de una forma entendible
*/
private String HexToString(byte[] arregloEncriptado) {
 String textoEncriptado = "";
 for (int i = 0; i < arregloEncriptado.length; i++) {
  int aux = arregloEncriptado[i] & 0xff;
  if (aux < 16) {
   textoEncriptado = textoEncriptado.concat("0");
  }
  textoEncriptado = textoEncriptado.concat(Integer.toHexString(aux));
 }
 return textoEncriptado;
}
 
6.- Crearemos nuestro metodo StringToHex
/**
Creo un arreglo de bytes, del tamaño de mi cadena / 2
(por eso el agregar un 0 si hera menor a 16 en el metodo de HexToString)

Por medio de un for recorro a mi arreglo

Dentro del fofr en la variable index guardare la posicion * 2, esto para siempre
tomar pares 2, 4, 6, etc...

En la variable aux guardo un subString de index a index+2 (0-2,2-4) siempre sera en pares, no hay fallas por que el metodo HexToString me retorno una cadena par

En una variable int, guardo el parseo del segmento de cadena, con base 16 (Hexadecimal)

Para finalizar en la posocion correspondiente de mi arreglo de bytes hago un cast
para guardar mi variable entera

Finalmente retornaria un arreglo de bytes identico al que recibio el metodo
HexToString, por tanto obtengo mi arreglo encriptado :)
*/
private byte[] StringToHex(String encriptado) {
 byte[] enBytes = new byte[encriptado.length() / 2];
 for (int i = 0; i < enBytes.length; i++) {
  int index = i * 2;
  String aux = encriptado.substring(index, index + 2);
  int v = Integer.parseInt(aux, 16);
  enBytes[i] = (byte) v;
 }
 return enBytes;
}
7.- Crearemos nuestro metodo Desencripcion
/**
Del mismo modo delego las excepciones a un nivel superior

Obtengo un arreglo de bytes a partir de mi key

Se lo asigno junto con el algoritmo a la clase SecretKeySpec.

Obtengo una instancia de Chiper en base al algoritmo

A mi variable chiper la pongo en modo desencripcion y le paso mi key secret

Obtengo un arreglo de bytes a partir de mi cadena encriptada

Al final creo un String a partir de mi arreglo de bytes

Esto no lo hice antes, como algunos ejemplos de la red, por que ? 
por que de este modo si lo hiciera desde el metodo encriptar, habria algunos
caracteres que no se podrian representar, por lo tanto se perderia
la integridad de la informacion.

Es mas pueden intentar realizar el programa sin utilizar HexToString y StringToHex
haciendo las conversiones del tipo new String(byte[]) y no trabajaria su codigo
*/
public String desencriptar(String encriptado) throws InvalidKeyException,
  IllegalBlockSizeException, BadPaddingException,
  UnsupportedEncodingException, NoSuchAlgorithmException,
  NoSuchPaddingException 
{
 byte[] raw = StringToHex(aesKey.getEncoded());
 SecretKeySpec skeySpec = new SecretKeySpec(raw, ALGORITMO);
 Cipher cipher = Cipher.getInstance(ALGORITMO);  
 cipher.init(Cipher.DECRYPT_MODE, skeySpec);
 byte[] original = cipher.doFinal(StringToHex(encriptado));
 String originalString = new String(original);
 return originalString;
}
8.- Crearemos nuestro Implementacion
public static void main(String args[]) throws Exception {
 AESKey aesKey = new AESKey();
  
 AESEncriptacion tmp = new AESEncriptacion();
 aesKey = tmp.generaKey();
 
 AESEncriptacion ejemplo = new AESEncriptacion(aesKey);

 String encriptado = ejemplo.encripta("123456789012345678");
 String desencriptado = ejemplo.desencriptar(encriptado);

 System.out.println(encriptado);
 System.out.println(desencriptado);
 }



9.- Se me olvidava, te coloco los importes por si las moscas :)

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

NOTA: Al final De una Forma Ligeramente Simple podemos encriptar utilizando el Algoritmo AES, En posteriores post colocare otras formas de encripcion

Sin miedo ! ! !

14 comentarios:

  1. hola, este codigo esta muy bien, pero hay muchos errores... por ejemplo en la linia 17 del paso 3. subid codigo depurado, porque si no la gente pierde mucho tiempo. gracias

    ResponderEliminar
  2. /**
    *
    * @author Menta
    */
    public class AlgoritmoAes {

    public static void main(String[] args) {
    AESKey aesKey = new AESKey();

    AESEncriptacion tmp = new AESEncriptacion(aesKey);
    aesKey = tmp.generaKey();
    AESEncriptacion ejemplo = new AESEncriptacion(aesKey);

    String encriptado = ejemplo.encripta("123456789012345678");
    String desencriptado = ejemplo.desencriptar(encriptado);

    System.out.println(encriptado);
    System.out.println(desencriptado);
    }
    }

    Te pongo el codigo depurado correctamente.
    Aitor_@hotmail.es
    www.supercodigo.es

    ResponderEliminar
  3. public class AESEncriptacion {

    private final String ALGORITMO = "AES";//algoritmo (si cambia la imprementacion tambien)
    private final int LONGITUD = 128;//longitud de la llave ()
    private final String CODIFICACION = "UTF-8";//como se convertira a byte, esto sera mas adelante
    private AESKey aesKey;

    public AESEncriptacion(AESKey aesKey) {
    this.aesKey = aesKey;
    }

    public AESKey generaKey() {
    KeyGenerator kgen = null;
    try {
    kgen = KeyGenerator.getInstance(ALGORITMO);
    } catch (NoSuchAlgorithmException ex) {
    error(ex);
    }
    kgen.init(LONGITUD);
    SecretKey skey = kgen.generateKey();
    aesKey = new AESKey();
    aesKey.setEncoded(HexToString(skey.getEncoded()));
    return aesKey;
    }

    public String encripta(String cadena) {
    String encriptado = null;
    try {
    byte[] raw = StringToHex(aesKey.getEncoded());
    SecretKeySpec skeySpec = new SecretKeySpec(raw, ALGORITMO);
    Cipher cipher = Cipher.getInstance(ALGORITMO);
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(cadena.getBytes(CODIFICACION));
    encriptado = HexToString(encrypted);

    } catch (IllegalBlockSizeException ex) {
    error(ex);
    } catch (BadPaddingException ex) {
    error(ex);
    } catch (UnsupportedEncodingException ex) {
    error(ex);
    } catch (InvalidKeyException ex) {
    error(ex);
    } catch (NoSuchAlgorithmException ex) {
    error(ex);
    } catch (NoSuchPaddingException ex) {
    error(ex);
    }
    return encriptado;
    }

    private String HexToString(byte[] arregloEncriptado) {
    String textoEncriptado = "";
    for (int i = 0; i < arregloEncriptado.length; i++) {
    int aux = arregloEncriptado[i] & 0xff;
    if (aux < 16) {
    textoEncriptado = textoEncriptado.concat("0");
    }
    textoEncriptado = textoEncriptado.concat(Integer.toHexString(aux));
    }
    return textoEncriptado;
    }

    private byte[] StringToHex(String encriptado) {
    byte[] enBytes = new byte[encriptado.length() / 2];
    for (int i = 0; i < enBytes.length; i++) {
    int index = i * 2;
    String aux = encriptado.substring(index, index + 2);
    int v = Integer.parseInt(aux, 16);
    enBytes[i] = (byte) v;
    }
    return enBytes;
    }

    public String desencriptar(String encriptado) {
    String originalString = null;
    try {
    byte[] raw = StringToHex(aesKey.getEncoded());
    SecretKeySpec skeySpec = new SecretKeySpec(raw, ALGORITMO);
    Cipher cipher = Cipher.getInstance(ALGORITMO);
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    byte[] original = cipher.doFinal(StringToHex(encriptado));
    originalString = new String(original);

    } catch (IllegalBlockSizeException ex) {
    error(ex);
    } catch (BadPaddingException ex) {
    error(ex);
    } catch (InvalidKeyException ex) {
    error(ex);
    } catch (NoSuchAlgorithmException ex) {
    error(ex);
    } catch (NoSuchPaddingException ex) {
    error(ex);
    }
    return originalString;
    }
    private void error(Exception ex) {
    System.err.print(ex.getMessage());
    }
    }
    Aitor_@hotmail.es
    www.supercodigo.es

    ResponderEliminar
  4. los imports de la clase anterior

    import java.io.UnsupportedEncodingException;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;
    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.KeyGenerator;
    import javax.crypto.NoSuchPaddingException;
    import javax.crypto.SecretKey;
    import javax.crypto.spec.SecretKeySpec;

    ResponderEliminar
  5. public class AESKey {
    private String encoded;

    public String getEncoded() {
    return encoded;
    }

    public void setEncoded(String encoded) {
    this.encoded = encoded;
    }
    }

    Aitor_@hotmail.es
    www.supercodigo.es

    ResponderEliminar
  6. No funciona... si utilizo la variable tmp.. para desencriptar "dc2c6001eb5a83ed4730faa6e13b566c" no lo hace simplemente devuelve null... solo funciona en caso de que el texto que vaya a desencriptar sea un texto que ya encripte anteriormente usando esa misma variable.. lo cual no me satisface la necesidad.. porque estoy leyendo lo encriptado desde la bd.

    ResponderEliminar
  7. Ese es el punto, solo poder desencriptar con la misma semilla con la cual encriptaste la información, de otro modo se pierde el sentido de encriptar algo que con cualquier semilla pueda ser decodificado.

    En algún lugar debe estar la semilla con la cual se encripto la información de tu bd. si no es el mismo caso. para eso tendrías que utilizar un método de desencriptacion que es cosa diferente a lo escrito en esta entrada.

    saludos.

    ResponderEliminar
  8. como encripto un texto con clave??

    ResponderEliminar
  9. con clave se esta encriptando 123456789012345678

    ResponderEliminar
    Respuestas
    1. pero en donde puedo modificar la clave?, o en este caso cual es la clave?

      Eliminar
  10. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
    Respuestas
    1. Que tal gracias por pasar por el blog !, intente replicar el error pero no pude hacerlo, lo que si es que vi 2 errores en el código original.

      Por lo tanto en unos instantes subiré la clase completa ya arreglada y comentando donde cometí errores la ves que hice este post

      Eliminar
    2. ya esta la actualización del código, y gracias por la observación.

      Eliminar
    3. Muchas gracias voy a probar de nuevo, y te comento. :)

      Eliminar