Cifrado basado en contraseñas Java AES de 256 bits

Necesito implementar cifrado AES de 256 bits, pero todos los ejemplos que he encontrado en línea usan un “KeyGenerator” para generar una clave de 256 bits, pero me gustaría utilizar mi propia clave de acceso. ¿Cómo puedo crear mi propia clave? He intentado rellenarlo a 256 bits, pero luego aparece un error que dice que la clave es demasiado larga. Tengo el parche de jurisdicción ilimitado instalado, entonces ese no es el problema 🙂

Es decir. El KeyGenerator se ve así …

// Get the KeyGenerator KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128); // 192 and 256 bits may not be available // Generate the secret key specs. SecretKey skey = kgen.generateKey(); byte[] raw = skey.getEncoded(); 

Código tomado de aquí

EDITAR

De hecho, estaba rellenando la contraseña en 256 bytes, no bits, que es demasiado larga. El siguiente es un código que estoy usando ahora que tengo más experiencia con esto.

 byte[] key = null; // TODO byte[] input = null; // TODO byte[] output = null; SecretKeySpec keySpec = null; keySpec = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec); output = cipher.doFinal(input) 

Los bits “TODO” que debes hacer tú mismo 🙂

Comparta la password (a char[] ) y salt (un byte[] -8 bytes seleccionado por un SecureRandom hace una buena sal, que no necesita mantenerse en secreto) con el destinatario 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 algún lugar) 65536 y 256 son el conteo de iteración de derivación de clave y el tamaño de clave, respectivamente.

La función de derivación de claves se itera para requerir un esfuerzo de cálculo significativo, y eso evita que los atacantes intenten rápidamente muchas contraseñas diferentes. El conteo de iteraciones se puede cambiar dependiendo de los recursos informáticos disponibles.

El tamaño de la clave se puede reducir a 128 bits, que aún se considera una encriptación “fuerte”, pero no proporciona un margen de seguridad si se descubren ataques que debilitan el AES.

Utilizado con un modo de encadenamiento de bloque adecuado, la misma clave derivada se puede utilizar para encriptar muchos mensajes. En 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 de 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 incluía soporte API para los modos de cifrado AEAD, y el proveedor “SunJCE” incluido con las distribuciones OpenJDK y Oracle implementa estos comenzando con Java 8. Se recomienda encarecidamente uno de estos modos en lugar de CBC; protegerá la integridad de los datos así como su privacidad.


Una java.security.InvalidKeyException con el mensaje “Tamaño de clave ilegal o parámetros predeterminados” significa que la fuerza 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 bajo ${jdk}/jre/lib/security

Según la descripción del problema, parece que los archivos de política no están instalados correctamente. Los sistemas pueden tener múltiples tiempos de ejecución de Java; Verifique dos veces para asegurarse de que se está utilizando la ubicación correcta.

Considere usar el Módulo criptográfico Spring Security

El módulo Spring Security Crypto brinda soporte para cifrado simétrico, generación de claves y encoding de contraseñas. El código se distribuye como parte del módulo principal, pero no tiene dependencias con ningún otro código Spring Security (o Spring).

Proporciona una abstracción simple para el cifrado y parece coincidir con lo que se requiere aquí,

El método de cifrado “estándar” es AES de 256 bits que usa PBKDF2 de PKCS # 5 (Función de derivación de clave basada en contraseña n. ° 2). Este método requiere Java 6. La contraseña utilizada para generar el SecretKey debe mantenerse en un lugar seguro y no ser compartida. La sal se usa para evitar ataques de diccionario contra la clave en caso de que sus datos cifrados se vean comprometidos. También se aplica un vector de inicialización aleatorio de 16 bytes, por lo que cada mensaje cifrado es único.

Una mirada a las partes internas revela una estructura similar a la respuesta de Erickson .

Como se señala en la pregunta, esto también requiere la Política de Jurisdicción de Fortaleza Ilimitada de Java Cryptography Extension (JCE) (de lo contrario, encontrará InvalidKeyException: Illegal Key Size ). Se puede descargar para Java 6 , Java 7 y Java 8 .

Ejemplo de uso

 import org.springframework.security.crypto.encrypt.Encryptors; import org.springframework.security.crypto.encrypt.TextEncryptor; import org.springframework.security.crypto.keygen.KeyGenerators; public class CryptoExample { public static void main(String[] args) { final String password = "I AM SHERLOCKED"; final String salt = KeyGenerators.string().generateKey(); TextEncryptor encryptor = Encryptors.text(password, salt); System.out.println("Salt: \"" + salt + "\""); String textToEncrypt = "*royal secrets*"; System.out.println("Original text: \"" + textToEncrypt + "\""); String encryptedText = encryptor.encrypt(textToEncrypt); System.out.println("Encrypted text: \"" + encryptedText + "\""); // Could reuse encryptor but wanted to show reconstructing TextEncryptor TextEncryptor decryptor = Encryptors.text(password, salt); String decryptedText = decryptor.decrypt(encryptedText); System.out.println("Decrypted text: \"" + decryptedText + "\""); if(textToEncrypt.equals(decryptedText)) { System.out.println("Success: decrypted text matches"); } else { System.out.println("Failed: decrypted text does not match"); } } } 

Y muestra de salida,

 Sal: "feacbc02a3a697b0"
 Texto original: "* secretos reales *"
 Texto cifrado: "7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a" 
 Texto descifrado: "* secretos reales *"
 Éxito: coincidencias de texto descifrado

Después de leer las sugerencias de Erickson, y de recostackr lo que pude de otras publicaciones y este ejemplo aquí , intenté actualizar el código de Doug con los cambios recomendados. Siéntete libre de editar para mejorarlo.

  • El vector de inicialización ya no está arreglado
  • la clave de encriptación se deriva usando el código de erickson
  • Sal de 8 bytes se genera en setupEncrypt () usando SecureRandom ()
  • la clave de descifrado se genera a partir de la contraseña y la contraseña de cifrado
  • cifrado descifrado se genera a partir de la clave de descifrado y el vector de inicialización
  • eliminado el twiddling hexadecimal en lugar del códec org.apache.commons. Rutinas hexadecimales

Algunas notas: Esto usa una clave de encriptación de 128 bits: java aparentemente no hará el cifrado de 256 bits listo para usar. La implementación de 256 requiere la instalación de algunos archivos adicionales en el directorio de instalación de Java.

Además, no soy una persona criptográfica. Ten cuidado.

 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidParameterSpecException; import java.security.spec.KeySpec; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; public class Crypto { String mPassword = null; public final static int SALT_LEN = 8; byte [] mInitVec = null; byte [] mSalt = null; Cipher mEcipher = null; Cipher mDecipher = null; private final int KEYLEN_BITS = 128; // see notes below where this is used. private final int ITERATIONS = 65536; private final int MAX_FILE_BUF = 1024; /** * create an object with just the passphrase from the user. Don't do anything else yet * @param password */ public Crypto (String password) { mPassword = password; } /** * return the generated salt for this object * @return */ public byte [] getSalt () { return (mSalt); } /** * return the initialization vector created from setupEncryption * @return */ public byte [] getInitVec () { return (mInitVec); } /** * debug/print messages * @param msg */ private void Db (String msg) { System.out.println ("** Crypt ** " + msg); } /** * this must be called after creating the initial Crypto object. It creates a salt of SALT_LEN bytes * and generates the salt bytes using secureRandom(). The encryption secret key is created * along with the initialization vectory. The member variable mEcipher is created to be used * by the class later on when either creating a CipherOutputStream, or encrypting a buffer * to be written to disk. * * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws NoSuchPaddingException * @throws InvalidParameterSpecException * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws UnsupportedEncodingException * @throws InvalidKeyException */ public void setupEncrypt () throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidKeyException { SecretKeyFactory factory = null; SecretKey tmp = null; // crate secureRandom salt and store as member var for later use mSalt = new byte [SALT_LEN]; SecureRandom rnd = new SecureRandom (); rnd.nextBytes (mSalt); Db ("generated salt :" + Hex.encodeHexString (mSalt)); factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); /* Derive the key, given password and salt. * * in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security" * The end user must also install them (not compiled in) so beware. * see here: http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml */ KeySpec spec = new PBEKeySpec (mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS); tmp = factory.generateSecret (spec); SecretKey secret = new SecretKeySpec (tmp.getEncoded(), "AES"); /* Create the Encryption cipher object and store as a member variable */ mEcipher = Cipher.getInstance ("AES/CBC/PKCS5Padding"); mEcipher.init (Cipher.ENCRYPT_MODE, secret); AlgorithmParameters params = mEcipher.getParameters (); // get the initialization vectory and store as member var mInitVec = params.getParameterSpec (IvParameterSpec.class).getIV(); Db ("mInitVec is :" + Hex.encodeHexString (mInitVec)); } /** * If a file is being decrypted, we need to know the pasword, the salt and the initialization vector (iv). * We have the password from initializing the class. pass the iv and salt here which is * obtained when encrypting the file initially. * * @param initvec * @param salt * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws NoSuchPaddingException * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws DecoderException */ public void setupDecrypt (String initvec, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, DecoderException { SecretKeyFactory factory = null; SecretKey tmp = null; SecretKey secret = null; // since we pass it as a string of input, convert to a actual byte buffer here mSalt = Hex.decodeHex (salt.toCharArray ()); Db ("got salt " + Hex.encodeHexString (mSalt)); // get initialization vector from passed string mInitVec = Hex.decodeHex (initvec.toCharArray ()); Db ("got initvector :" + Hex.encodeHexString (mInitVec)); /* Derive the key, given password and salt. */ // in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security" // The end user must also install them (not compiled in) so beware. // see here: // http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS); tmp = factory.generateSecret(spec); secret = new SecretKeySpec(tmp.getEncoded(), "AES"); /* Decrypt the message, given derived key and initialization vector. */ mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec)); } /** * This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt(). * Pass two file objects representing the actual input (cleartext) and output file to be encrypted. * * there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran * into uncertain problems with that. * * @param input - the cleartext file to be encrypted * @param output - the encrypted data file * @throws IOException * @throws IllegalBlockSizeException * @throws BadPaddingException */ public void WriteEncryptedFile (File input, File output) throws IOException, IllegalBlockSizeException, BadPaddingException { FileInputStream fin; FileOutputStream fout; long totalread = 0; int nread = 0; byte [] inbuf = new byte [MAX_FILE_BUF]; fout = new FileOutputStream (output); fin = new FileInputStream (input); while ((nread = fin.read (inbuf)) > 0 ) { Db ("read " + nread + " bytes"); totalread += nread; // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0 // and results in full blocks of MAX_FILE_BUF being written. byte [] trimbuf = new byte [nread]; for (int i = 0; i < nread; i++) trimbuf[i] = inbuf[i]; // encrypt the buffer using the cipher obtained previosly byte [] tmp = mEcipher.update (trimbuf); // I don't think this should happen, but just in case.. if (tmp != null) fout.write (tmp); } // finalize the encryption since we've done it in blocks of MAX_FILE_BUF byte [] finalbuf = mEcipher.doFinal (); if (finalbuf != null) fout.write (finalbuf); fout.flush(); fin.close(); fout.close(); fout.close (); Db ("wrote " + totalread + " encrypted bytes"); } /** * Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out * to disk as (output) File. * * I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting * and still have a correctly decrypted file in the end. Seems to work so left it in. * * @param input - File object representing encrypted data on disk * @param output - File object of cleartext data to write out after decrypting * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws IOException */ public void ReadEncryptedFile (File input, File output) throws IllegalBlockSizeException, BadPaddingException, IOException { FileInputStream fin; FileOutputStream fout; CipherInputStream cin; long totalread = 0; int nread = 0; byte [] inbuf = new byte [MAX_FILE_BUF]; fout = new FileOutputStream (output); fin = new FileInputStream (input); // creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt() cin = new CipherInputStream (fin, mDecipher); while ((nread = cin.read (inbuf)) > 0 ) { Db ("read " + nread + " bytes"); totalread += nread; // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0 byte [] trimbuf = new byte [nread]; for (int i = 0; i < nread; i++) trimbuf[i] = inbuf[i]; // write out the size-adjusted buffer fout.write (trimbuf); } fout.flush(); cin.close(); fin.close (); fout.close(); Db ("wrote " + totalread + " encrypted bytes"); } /** * adding main() for usage demonstration. With member vars, some of the locals would not be needed */ public static void main(String [] args) { // create the input.txt file in the current directory before continuing File input = new File ("input.txt"); File eoutput = new File ("encrypted.aes"); File doutput = new File ("decrypted.txt"); String iv = null; String salt = null; Crypto en = new Crypto ("mypassword"); /* * setup encryption cipher using password. print out iv and salt */ try { en.setupEncrypt (); iv = Hex.encodeHexString (en.getInitVec ()).toUpperCase (); salt = Hex.encodeHexString (en.getSalt ()).toUpperCase (); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidParameterSpecException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } /* * write out encrypted file */ try { en.WriteEncryptedFile (input, eoutput); System.out.printf ("File encrypted to " + eoutput.getName () + "\niv:" + iv + "\nsalt:" + salt + "\n\n"); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } /* * decrypt file */ Crypto dc = new Crypto ("mypassword"); try { dc.setupDecrypt (iv, salt); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (DecoderException e) { e.printStackTrace(); } /* * write out decrypted file */ try { dc.ReadEncryptedFile (eoutput, doutput); System.out.println ("decryption finished to " + doutput.getName ()); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } 

Implementé la respuesta de Erickson en una clase realmente simple:
Clase de cifrado / descifrado Java AES de 256 bits

Si obtiene la java.security.InvalidKeyException , debe instalar los archivos de la política de jurisdicción de fuerza ilimitada Java Cryptography Extension (JCE):

  • Enlace de Java 6
  • Enlace de Java 7

Solo coloque los flasks en su {JDK HOME}\jre\lib\security

Generar su propia clave a partir de una matriz de bytes es fácil:

 byte[] raw = ...; // 32 bytes in size for a 256 bit key Key skey = new javax.crypto.spec.SecretKeySpec(raw, "AES"); 

Pero crear una clave de 256 bits no es suficiente. Si el generador de claves no puede generar claves de 256 bits para usted, entonces la clase Cipher probablemente tampoco sea compatible con AES de 256 bits. Usted dice que tiene instalado el parche de jurisdicción ilimitada, por lo que debe ser compatible con el cifrado AES-256 (pero las claves de 256 bits también deberían serlo, por lo que podría tratarse de un problema de configuración).

 Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, skey); byte[] encrypted = cipher.doFinal(plainText.getBytes()); 

Una solución para la falta de compatibilidad con AES-256 es tomar alguna implementación de AES-256 disponible libremente, y usarla como un proveedor personalizado. Esto implica crear su propia subclase de Provider y usarla con Cipher.getInstance(String, Provider) . Pero esto puede ser un proceso complicado.

Lo que he hecho en el pasado es hash la clave a través de algo como SHA256, luego extrae los bytes del hash en el byte clave [].

Después de tener su byte [], simplemente puede hacer:

 SecretKeySpec key = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encryptedBytes = cipher.doFinal(clearText.getBytes()); 

Agregando a las ediciones de @ Wufoo, la siguiente versión usa InputStreams en lugar de archivos para facilitar el trabajo con una variedad de archivos. También almacena IV y Salt al principio del archivo, por lo que solo se debe rastrear la contraseña. Como IV y Salt no necesitan ser secretos, esto hace la vida un poco más fácil.

 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.AlgorithmParameters; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidParameterSpecException; import java.security.spec.KeySpec; import java.util.logging.Level; import java.util.logging.Logger; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; public class AES { public final static int SALT_LEN = 8; static final String HEXES = "0123456789ABCDEF"; String mPassword = null; byte[] mInitVec = null; byte[] mSalt = new byte[SALT_LEN]; Cipher mEcipher = null; Cipher mDecipher = null; private final int KEYLEN_BITS = 128; // see notes below where this is used. private final int ITERATIONS = 65536; private final int MAX_FILE_BUF = 1024; /** * create an object with just the passphrase from the user. Don't do anything else yet * @param password */ public AES(String password) { mPassword = password; } public static String byteToHex(byte[] raw) { if (raw == null) { return null; } final StringBuilder hex = new StringBuilder(2 * raw.length); for (final byte b : raw) { hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F))); } return hex.toString(); } public static byte[] hexToByte(String hexString) { int len = hexString.length(); byte[] ba = new byte[len / 2]; for (int i = 0; i < len; i += 2) { ba[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16)); } return ba; } /** * debug/print messages * @param msg */ private void Db(String msg) { System.out.println("** Crypt ** " + msg); } /** * This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt(). * Pass two file objects representing the actual input (cleartext) and output file to be encrypted. * * there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran * into uncertain problems with that. * * @param input - the cleartext file to be encrypted * @param output - the encrypted data file * @throws IOException * @throws IllegalBlockSizeException * @throws BadPaddingException */ public void WriteEncryptedFile(InputStream inputStream, OutputStream outputStream) throws IOException, IllegalBlockSizeException, BadPaddingException { try { long totalread = 0; int nread = 0; byte[] inbuf = new byte[MAX_FILE_BUF]; SecretKeyFactory factory = null; SecretKey tmp = null; // crate secureRandom salt and store as member var for later use mSalt = new byte[SALT_LEN]; SecureRandom rnd = new SecureRandom(); rnd.nextBytes(mSalt); Db("generated salt :" + byteToHex(mSalt)); factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); /* * Derive the key, given password and salt. * * in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security" * The end user must also install them (not compiled in) so beware. * see here: http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml */ KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS); tmp = factory.generateSecret(spec); SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); /* * Create the Encryption cipher object and store as a member variable */ mEcipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); mEcipher.init(Cipher.ENCRYPT_MODE, secret); AlgorithmParameters params = mEcipher.getParameters(); // get the initialization vectory and store as member var mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV(); Db("mInitVec is :" + byteToHex(mInitVec)); outputStream.write(mSalt); outputStream.write(mInitVec); while ((nread = inputStream.read(inbuf)) > 0) { Db("read " + nread + " bytes"); totalread += nread; // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0 // and results in full blocks of MAX_FILE_BUF being written. byte[] trimbuf = new byte[nread]; for (int i = 0; i < nread; i++) { trimbuf[i] = inbuf[i]; } // encrypt the buffer using the cipher obtained previosly byte[] tmpBuf = mEcipher.update(trimbuf); // I don't think this should happen, but just in case.. if (tmpBuf != null) { outputStream.write(tmpBuf); } } // finalize the encryption since we've done it in blocks of MAX_FILE_BUF byte[] finalbuf = mEcipher.doFinal(); if (finalbuf != null) { outputStream.write(finalbuf); } outputStream.flush(); inputStream.close(); outputStream.close(); outputStream.close(); Db("wrote " + totalread + " encrypted bytes"); } catch (InvalidKeyException ex) { Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex); } catch (InvalidParameterSpecException ex) { Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchAlgorithmException ex) { Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchPaddingException ex) { Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex); } catch (InvalidKeySpecException ex) { Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex); } } /** * Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out * to disk as (output) File. * * I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting * and still have a correctly decrypted file in the end. Seems to work so left it in. * * @param input - File object representing encrypted data on disk * @param output - File object of cleartext data to write out after decrypting * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws IOException */ public void ReadEncryptedFile(InputStream inputStream, OutputStream outputStream) throws IllegalBlockSizeException, BadPaddingException, IOException { try { CipherInputStream cin; long totalread = 0; int nread = 0; byte[] inbuf = new byte[MAX_FILE_BUF]; // Read the Salt inputStream.read(this.mSalt); Db("generated salt :" + byteToHex(mSalt)); SecretKeyFactory factory = null; SecretKey tmp = null; SecretKey secret = null; factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS); tmp = factory.generateSecret(spec); secret = new SecretKeySpec(tmp.getEncoded(), "AES"); /* Decrypt the message, given derived key and initialization vector. */ mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // Set the appropriate size for mInitVec by Generating a New One AlgorithmParameters params = mDecipher.getParameters(); mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV(); // Read the old IV from the file to mInitVec now that size is set. inputStream.read(this.mInitVec); Db("mInitVec is :" + byteToHex(mInitVec)); mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec)); // creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt() cin = new CipherInputStream(inputStream, mDecipher); while ((nread = cin.read(inbuf)) > 0) { Db("read " + nread + " bytes"); totalread += nread; // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0 byte[] trimbuf = new byte[nread]; for (int i = 0; i < nread; i++) { trimbuf[i] = inbuf[i]; } // write out the size-adjusted buffer outputStream.write(trimbuf); } outputStream.flush(); cin.close(); inputStream.close(); outputStream.close(); Db("wrote " + totalread + " encrypted bytes"); } catch (Exception ex) { Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex); } } /** * adding main() for usage demonstration. With member vars, some of the locals would not be needed */ public static void main(String[] args) { // create the input.txt file in the current directory before continuing File input = new File("input.txt"); File eoutput = new File("encrypted.aes"); File doutput = new File("decrypted.txt"); String iv = null; String salt = null; AES en = new AES("mypassword"); /* * write out encrypted file */ try { en.WriteEncryptedFile(new FileInputStream(input), new FileOutputStream(eoutput)); System.out.printf("File encrypted to " + eoutput.getName() + "\niv:" + iv + "\nsalt:" + salt + "\n\n"); } catch (IllegalBlockSizeException | BadPaddingException | IOException e) { e.printStackTrace(); } /* * decrypt file */ AES dc = new AES("mypassword"); /* * write out decrypted file */ try { dc.ReadEncryptedFile(new FileInputStream(eoutput), new FileOutputStream(doutput)); System.out.println("decryption finished to " + doutput.getName()); } catch (IllegalBlockSizeException | BadPaddingException | IOException e) { e.printStackTrace(); } } } 

Use this class for encryption. Funciona.

 public class ObjectCrypter { public static byte[] encrypt(byte[] ivBytes, byte[] keyBytes, byte[] mes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException { AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes); SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = null; cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec); return cipher.doFinal(mes); } public static byte[] decrypt(byte[] ivBytes, byte[] keyBytes, byte[] bytes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException, ClassNotFoundException { AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes); SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec); return cipher.doFinal(bytes); } 

}

And these are ivBytes and a random key;

 String key = "e8ffc7e56311679f12b6fc91aa77a5eb"; byte[] ivBytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; keyBytes = key.getBytes("UTF-8"); 

Consider using Encryptor4j

First make sure you have Unlimited Strength Jurisdiction Policy files installed before your proceed so that you can use 256-bit AES keys.

Then do the following:

 String password = "mysupersecretpassword"; Key key = KeyFactory.AES.keyFromPassword(password.toCharArray()); Encryptor encryptor = new Encryptor(key, "AES/CBC/PKCS7Padding", 16); 

You can now use the encryptor to encrypt your message. You can also perform streaming encryption if you’d like. It automatically generates and prepends a secure IV for your convenience.

If it’s a file that you wish to compress take a look at this answer Encrypting a large file with AES using JAVA for an even simpler approach.