Comparta el password
(a char[]
) y salt
(un byte[]
—8 bytes seleccionados por un SecureRandom
hace una buena sal — que no necesita mantenerse en secreto) con el receptor fuera de banda. Luego, para obtener una buena clave de esta información:
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Los números mágicos (que podrían definirse como constantes en alguna parte) 65536 y 256 son el recuento de iteración de derivación de clave y el tamaño de clave, respectivamente.
La función de derivación de clave se repite para requerir un esfuerzo computacional significativo, y eso evita que los atacantes prueben rápidamente muchas contraseñas diferentes. El recuento de iteraciones se puede cambiar según los recursos informáticos disponibles.
El tamaño de la clave se puede reducir a 128 bits, lo que todavía se considera cifrado "fuerte", pero no da mucho margen de seguridad si se descubren ataques que debilitan AES.
Utilizado con un modo de encadenamiento de bloques adecuado, la misma clave derivada se puede utilizar para cifrar muchos mensajes. En Cipher Block Chaining (CBC) , se genera un vector de inicialización aleatorio (IV) para cada mensaje, produciendo un texto de cifrado diferente incluso si el texto plano es idéntico. CBC puede no ser el modo más seguro disponible para usted (vea AEAD a continuación); Hay muchos otros modos con diferentes propiedades de seguridad, pero todos usan una entrada aleatoria similar. En cualquier caso, las salidas de cada operación de cifrado son el texto cifrado y el vector de inicialización:
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8"));
Almacene el ciphertext
y el iv
. En el descifrado, SecretKey
se regenera exactamente de la misma manera, utilizando la contraseña con los mismos parámetros de sal e iteración. Inicialice el cifrado con esta clave y el vector de inicialización almacenado con el mensaje:
/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext), "UTF-8");
System.out.println(plaintext);
Java 7 incluyó soporte API para modos de cifrado AEAD , y el proveedor "SunJCE" incluido con las distribuciones OpenJDK y Oracle implementa estos a partir de Java 8. Se recomienda uno de estos modos en lugar de CBC; Protegerá la integridad de los datos y su privacidad.
A java.security.InvalidKeyException
con el mensaje "Tamaño de clave ilegal o parámetros predeterminados" significa que la intensidad de la criptografía es limitada; los archivos de política de jurisdicción de fuerza ilimitada no están en la ubicación correcta. En un JDK, deben colocarse debajo${jdk}/jre/lib/security
Según la descripción del problema, parece que los archivos de políticas no están instalados correctamente. Los sistemas pueden tener fácilmente múltiples tiempos de ejecución de Java; vuelva a verificar para asegurarse de que se esté utilizando la ubicación correcta.