Ejemplo simple de encriptación / descifrado de Java AES

¿Qué pasa con el siguiente ejemplo?

El problema es que la primera parte de la cadena descifrada no tiene sentido. Sin embargo, el rest está bien, me sale …

Result: `£eB6O geS  i are you? Have a nice day. 
 @Test public void testEncrypt() { try { String s = "Hello there. How are you? Have a nice day."; // Generate key KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128); SecretKey aesKey = kgen.generateKey(); // Encrypt cipher Cipher encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey); // Encrypt ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, encryptCipher); cipherOutputStream.write(s.getBytes()); cipherOutputStream.flush(); cipherOutputStream.close(); byte[] encryptedBytes = outputStream.toByteArray(); // Decrypt cipher Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded()); decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec); // Decrypt outputStream = new ByteArrayOutputStream(); ByteArrayInputStream inStream = new ByteArrayInputStream(encryptedBytes); CipherInputStream cipherInputStream = new CipherInputStream(inStream, decryptCipher); byte[] buf = new byte[1024]; int bytesRead; while ((bytesRead = cipherInputStream.read(buf)) >= 0) { outputStream.write(buf, 0, bytesRead); } System.out.println("Result: " + new String(outputStream.toByteArray())); } catch (Exception ex) { ex.printStackTrace(); } } 

Mucha gente, incluyéndome a mí, se enfrenta a muchos problemas para hacer que esto funcione debido a que falta información como, olvidar convertir a Base64, vectores de inicialización, juego de caracteres, etc. Así que pensé en hacer un código completamente funcional.

Espero que esto sea útil para todos ustedes: para comstackr, necesitan una jarra de códec de Apache Commons adicional, que está disponible aquí: http://commons.apache.org/proper/commons-codec/download_codec.cgi

 import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; public class Encryptor { public static String encrypt(String key, String initVector, String value) { try { IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); byte[] encrypted = cipher.doFinal(value.getBytes()); System.out.println("encrypted string: " + Base64.encodeBase64String(encrypted)); return Base64.encodeBase64String(encrypted); } catch (Exception ex) { ex.printStackTrace(); } return null; } public static String decrypt(String key, String initVector, String encrypted) { try { IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted)); return new String(original); } catch (Exception ex) { ex.printStackTrace(); } return null; } public static void main(String[] args) { String key = "Bar12345Bar12345"; // 128 bit key String initVector = "RandomInitVector"; // 16 bytes IV System.out.println(decrypt(key, initVector, encrypt(key, initVector, "Hello World"))); } } 

Aquí una solución sin Base64 Apache Commons Codec :

 import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; public class AdvancedEncryptionStandard { private byte[] key; private static final String ALGORITHM = "AES"; public AdvancedEncryptionStandard(byte[] key) { this.key = key; } /** * Encrypts the given plain text * * @param plainText The plain text to encrypt */ public byte[] encrypt(byte[] plainText) throws Exception { SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(plainText); } /** * Decrypts the given byte array * * @param cipherText The data to decrypt */ public byte[] decrypt(byte[] cipherText) throws Exception { SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(cipherText); } } 

Ejemplo de uso:

 byte[] encryptionKey = "MZygpewJsCpRrfOr".getBytes(StandardCharsets.UTF_8); byte[] plainText = "Hello world!".getBytes(StandardCharsets.UTF_8); AdvancedEncryptionStandard advancedEncryptionStandard = new AdvancedEncryptionStandard( encryptionKey); byte[] cipherText = advancedEncryptionStandard.encrypt(plainText); byte[] decryptedCipherText = advancedEncryptionStandard.decrypt(cipherText); System.out.println(new String(plainText)); System.out.println(new String(cipherText)); System.out.println(new String(decryptedCipherText)); 

Huellas dactilares:

 Hello world! դ;  LA+ ߙb* Hello world! 

A mi me parece que no estás tratando adecuadamente con tu Vector de Inicialización (IV). Ha pasado mucho tiempo desde la última vez que leí sobre AES, IVs y locking de cadenas, pero tu línea

 IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded()); 

no parece estar bien. En el caso de AES, puede pensar en el vector de inicialización como el “estado inicial” de una instancia de cifrado, y este estado es un poco de información que no puede obtener de su clave, sino del cálculo real del cifrado cifrado. (Se podría argumentar que si la IV se pudiera extraer de la clave, no serviría de nada, ya que la clave ya se le dio a la instancia de cifrado durante su fase de inicio).

Por lo tanto, debe obtener IV como un byte [] de la instancia de cifrado al final de su cifrado

  cipherOutputStream.close(); byte[] iv = encryptCipher.getIV(); 

y debe inicializar su Cipher en DECRYPT_MODE con este byte []:

  IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); 

Entonces, tu descifrado debería estar bien. Espero que esto ayude.

La IV que usa para descifrado es incorrecta. Reemplace este código

 //Decrypt cipher Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded()); decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec); 

Con este código

 //Decrypt cipher Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(encryptCipher.getIV()); decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec); 

Y eso debería resolver tu problema.


A continuación se incluye un ejemplo de una clase AES simple en Java. No recomiendo usar esta clase en entornos de producción, ya que puede no tener en cuenta todas las necesidades específicas de su aplicación.

 import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; public class AES { public static byte[] encrypt(final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException { return AES.transform(Cipher.ENCRYPT_MODE, keyBytes, ivBytes, messageBytes); } public static byte[] decrypt(final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException { return AES.transform(Cipher.DECRYPT_MODE, keyBytes, ivBytes, messageBytes); } private static byte[] transform(final int mode, final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException { final SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); final IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); byte[] transformedBytes = null; try { final Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); cipher.init(mode, keySpec, ivSpec); transformedBytes = cipher.doFinal(messageBytes); } catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) { e.printStackTrace(); } return transformedBytes; } public static void main(final String[] args) throws InvalidKeyException, InvalidAlgorithmParameterException { //Retrieved from a protected local file. //Do not hard-code and do not version control. final String base64Key = "ABEiM0RVZneImaq7zN3u/w=="; //Retrieved from a protected database. //Do not hard-code and do not version control. final String shadowEntry = "AAECAwQFBgcICQoLDA0ODw==:ZtrkahwcMzTu7e/WuJ3AZmF09DE="; //Extract the iv and the ciphertext from the shadow entry. final String[] shadowData = shadowEntry.split(":"); final String base64Iv = shadowData[0]; final String base64Ciphertext = shadowData[1]; //Convert to raw bytes. final byte[] keyBytes = Base64.getDecoder().decode(base64Key); final byte[] ivBytes = Base64.getDecoder().decode(base64Iv); final byte[] encryptedBytes = Base64.getDecoder().decode(base64Ciphertext); //Decrypt data and do something with it. final byte[] decryptedBytes = AES.decrypt(keyBytes, ivBytes, encryptedBytes); //Use non-blocking SecureRandom implementation for the new IV. final SecureRandom secureRandom = new SecureRandom(); //Generate a new IV. secureRandom.nextBytes(ivBytes); //At this point instead of printing to the screen, //one should replace the old shadow entry with the new one. System.out.println("Old Shadow Entry = " + shadowEntry); System.out.println("Decrytped Shadow Data = " + new String(decryptedBytes, StandardCharsets.UTF_8)); System.out.println("New Shadow Entry = " + Base64.getEncoder().encodeToString(ivBytes) + ":" + Base64.getEncoder().encodeToString(AES.encrypt(keyBytes, ivBytes, decryptedBytes))); } } 

Tenga en cuenta que AES no tiene nada que ver con la encoding, por lo que decidí manejarlo por separado y sin necesidad de bibliotecas de terceros.

A menudo es una buena idea confiar en la solución estándar proporcionada por la biblioteca:

 private static void stackOverflow15554296() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { // prepare key KeyGenerator keygen = KeyGenerator.getInstance("AES"); SecretKey aesKey = keygen.generateKey(); String aesKeyForFutureUse = Base64.getEncoder().encodeToString( aesKey.getEncoded() ); // cipher engine Cipher aesCipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // cipher input aesCipher.init(Cipher.ENCRYPT_MODE, aesKey); byte[] clearTextBuff = "Text to encode".getBytes(); byte[] cipherTextBuff = aesCipher.doFinal(clearTextBuff); // recreate key byte[] aesKeyBuff = Base64.getDecoder().decode(aesKeyForFutureUse); SecretKey aesDecryptKey = new SecretKeySpec(aesKeyBuff, "AES"); // decipher input aesCipher.init(Cipher.DECRYPT_MODE, aesDecryptKey); byte[] decipheredBuff = aesCipher.doFinal(cipherTextBuff); System.out.println(new String(decipheredBuff)); } 

Esto imprime “Texto para codificar”.

La solución se basa en Java Cryptography Architecture Reference Guide y https://stackoverflow.com/a/20591539/146745 answer.

Versión Runnable del Editor en línea: –

 import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; //import org.apache.commons.codec.binary.Base64; import java.util.Base64; public class Encryptor { public static String encrypt(String key, String initVector, String value) { try { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); byte[] encrypted = cipher.doFinal(value.getBytes()); //System.out.println("encrypted string: " // + Base64.encodeBase64String(encrypted)); //return Base64.encodeBase64String(encrypted); String s = new String(Base64.getEncoder().encode(encrypted)); return s; } catch (Exception ex) { ex.printStackTrace(); } return null; } public static String decrypt(String key, String initVector, String encrypted) { try { IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); byte[] original = cipher.doFinal(Base64.getDecoder().decode(encrypted)); return new String(original); } catch (Exception ex) { ex.printStackTrace(); } return null; } public static void main(String[] args) { String key = "Bar12345Bar12345"; // 128 bit key String initVector = "RandomInitVector"; // 16 bytes IV System.out.println(encrypt(key, initVector, "Hello World")); System.out.println(decrypt(key, initVector, encrypt(key, initVector, "Hello World"))); } }