Cyberspace

Gadgets y Tecnologia

Criptografía simétrica



Symmetric Cryptography

Consiste en cifrar y descifrar mensajes utilizando una misma clave. Dicha clave tiene que ser compartida entre las dos partes que se intercambian mensajes.

El mensaje es cifrado antes de ser enviado por el emisor utilizando uno de los algoritmos de clave simétrica y el receptor descifra el mensaje utilizando la misma clave y el mismo algoritmo.

La clave debe ser previamente intercambiada por un canal seguro. A partir de ese momento podemos comenzar a intercambiar mensajes.

Los algoritmos de clave simétrica más comunes son.

  • DES. Es un algoritmo débil, basado en claves de 56bits. Debe dejar de utilizarse.
  • 3DES. Basado en DES logra obtener claves de 128bits.
  • AES. Es el recomendado. Permite claves de 128, 192 y 256 bits.

A parte de para intercambiar mensajes entre dos partes, estos algoritmos de clave simétrica se utilizan en diferentes ámbitos, como

  • el cifrado de tokens en la autenticación OAuth 2.0
  • cifrado de ficheros de configuración
  • cifrado de datos sensibles de usuarios
  • etc

Generar una clave simétrica

A partir de una frase o contraseña podemos generar una clave simétrica. Cuando mayor sea el número de bits de la clave simétrica generada mayor seguridad tendremos de que no podrá ser descubierta por ataques de fuerza bruta.

Uno de los mecanismos más habituales para generar una clave simétrica es utilizar una frase o contraseña, y aplicarle la especificación PBKDF2 o Password Based Key Derivation Funtion 2.

PBKDF2 es una especificación que forma parte de las especificaciones de los estándares de criptográfica de clave pública.

PBKDF2 consiste en generar o derivar una nueva contraseña a partir una contraseña existente. Para ello se parte de la contraseña inicial y de una semilla, sobre los que se le aplica una función de criptografía del tipo Hash, y se repite este proceso muchas veces.

El resultado final es la clave simétrica. Normalmente la clave simétrica generada la veremos representada como una secuencia de bytes.

Esta secuencia de bytes la podemos guardar como un fichero binario, podemos convertirla en otros formados como puede ser hexadecimal y guardarla como texto, o utilizar otro formato que nos resulte más cómodo.

El objetivo final será poder recuperar de nuevo los bytes y utilizarlos para cifrar y descifrar los mensajes.

También podemos optar por generar siempre esta clave simétrica cuando queramos cifrar y descifrar mensajes. En este caso debemos utilizar PBKDF2 con la misma combinación de contraseña y semilla para obtener la misma clave simétrica.

Ejemplo.

Para la conversión de bytes a hexadecimal utilizamos el proyecto Apache Commons Codec.

import java.security.SecureRandom;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Hex;

public class SymmetricKey {

    public static final String PBKDF2 = "PBKDF2WithHmacSHA1";
    public static final String SYMMETRIC_ALGORITHM = "AES";
    public static final String ENCODING_MSG = "UTF-8";
    public static final int numeroDeIteraciones = 655536;
    /**
     * numeroDeBitsClaveGenerada
     *      256 y 192 bits nos da como resultado "Illegal key size or default parameters". 
     *      Para poder utilizar mas bits en la clave tenemos que instalar "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files" 
     * */
    public static final int numeroDeBitsClaveGenerada = 128; 
    public static final int numeroDeBitsSalt = 256;

    public static void main(String[] args) throws Exception {
        /* la frase o clave a utilizar para generar la clave simetrica */
        String passphrase = "Esta mi frase";
        SymmetricKey sk = new SymmetricKey();
        /* generamos la Salt o semilla */
        byte[] salt = sk.generateSalt();
        System.out.println("Numero de bits de la semilla o salt: " + salt.length*8);
        /* generamos la clave simetrica a partir de la frase y de la salt o semilla */
        byte[] key = sk.generateSymmetricKey(passphrase, salt);
        System.out.println("Numero de bits de la clave simetrica generada: " + key.length*8);
        /* Mensaje a cifrar y descifrar */
        String thisIsTheMessage = "Este es el mensaje o texto a cifrar!";
        /* ciframos el mensaje */
        System.out.println("Texto a cifrar: " + thisIsTheMessage);
        byte[] msgcipher = sk.cipher(key, thisIsTheMessage);
        System.out.println("Mensaje cifrado: " + new String(msgcipher, ENCODING_MSG));
        /* desciframos el mensaje */
        String msg = sk.decipher(key, msgcipher);
        System.out.println("Mensaje descifrado: " + msg);

        /* Conversiones a hexadecimal */
        String saltInHex = Hex.encodeHexString(salt);
        String keyInHex = Hex.encodeHexString(key);
        String msgCipherInHex = Hex.encodeHexString(msgcipher);
        System.out.println("Semila en hexadecimal: " + saltInHex);
        System.out.println("Clave simetrica en hexadecimal: " + keyInHex);
        System.out.println("Mensaje cifrado en hexadecimal: " + msgCipherInHex);
    }

    public byte[] cipher(byte[] key, String message) throws Exception {
        SecretKey secret = new SecretKeySpec(key, SYMMETRIC_ALGORITHM);
        Cipher cipher = Cipher.getInstance(SYMMETRIC_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secret);
        byte[] ciphertext = cipher.doFinal(message.getBytes(ENCODING_MSG));
        return ciphertext;
    }

    public String decipher(byte[] key, byte[] message) throws Exception {
        SecretKey secret = new SecretKeySpec(key, SYMMETRIC_ALGORITHM);
        Cipher cipher = Cipher.getInstance(SYMMETRIC_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secret);
        byte[] ciphertext = cipher.doFinal(message);
        return new String(ciphertext,ENCODING_MSG);
    }

    public byte[] generateSalt() {
        SecureRandom sr = new SecureRandom();
        byte[] bytes = new byte[numeroDeBitsSalt/8]; /* 256 bits = 32 bytes */
        sr.nextBytes(bytes);
        return bytes;
    }

    public byte[] generateSymmetricKey(String passphrase, byte[] salt) throws Exception {
        SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF2);
        KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), salt, numeroDeIteraciones, numeroDeBitsClaveGenerada);
        SecretKey derivedKey = factory.generateSecret(spec);
        SecretKey secret = new SecretKeySpec(derivedKey.getEncoded(), SYMMETRIC_ALGORITHM);
        return secret.getEncoded();
    }
}

Enlaces de interes

Escriba un comentario