Inicial bytes incorrecta después de Java AES/CBC descifrado

Lo que está mal con el siguiente ejemplo?

El problema es que la primera parte de la cadena descifrada es una tontería. Sin embargo, el resto está bien, yo…

Result: eB6OgeS��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();
  }
}
  • NO UTILICE NINGÚN TIPO DE RESPUESTA DE ESTA PREGUNTA EN PROYECTO SERIO ! Todos en el ejemplo proporcionado en esta pregunta son vulnerables a padding oracle y son en general muy malo el uso de la criptografía. Usted va a introducir la criptografía seria vulnerabilidad en su proyecto mediante el uso de cualquiera de los siguiente fragmento.
  • Con respecto a las siguientes citas: «no debería ser el desarrollo de su propia biblioteca de criptografía» y «el uso de un alto nivel de API que su marco de trabajo proporciona.» Aquí No se trata de que el desarrollo de su propia biblioteca de criptografía. Simplemente estamos utilizando la ya existente, de alto nivel de la API de que el marco de java proporciona. Usted sir son imprecisos.
  • Definitivamente no son de alto nivel «criptografía» de la API. Como una buena regla de oro, si usted tiene que escribir o copiar/pegar AES, usted no está usando un de alto nivel de la API de criptografía. Correctamente el montaje de AES con el algoritmo apropiado para prevenir el ataque criptográfico es duro y hay que evitar hacerlo si usted no tiene ninguna experiencia en la criptografía. Usted está a solo tiro en el pie sin darse cuenta.
  • Bastante justo punto sobre ejemplos inseguros, siéntase libre de dejar un comentario. En cuanto a esta pregunta va, yo estaba empezando y sólo necesitaba un poco de ayuda con una pregunta específica. Los buenos desarrolladores va a hacer la investigación necesaria en cuanto a cómo asegurar que algo es malo que los desarrolladores no.
  • Sólo porque ambos están de acuerdo no implica que ambos están en lo correcto. Los buenos desarrolladores saben la diferencia entre la envoltura de un alto nivel de API y la reescritura de un bajo nivel de API. los Buenos lectores se dará cuenta de que el OP me pidió una «simple java AES cifrar/descifrar el ejemplo» y eso es exactamente lo que él consiguió. Yo también no está de acuerdo con las otras respuestas, que es la razón por la que he publicado una respuesta de mi propia. Tal vez ustedes deben tratar de la misma y nos ilumine a todos con su experiencia.
  • He empezado a una discusión sobre la Meta en lugar. Siéntase libre de contribuir. Tenga en cuenta que no estoy de acuerdo en lo que realmente le preguntó. Puse la pregunta a continuación el código de ejemplo en la pregunta (yo no editar la pregunta en sí misma).
  • gracias por el enlace. He editado mi post.
  • Hice «no debería ser el desarrollo de su propia biblioteca de criptografía en el primer lugar». En serio seguir este consejo. Su marco (y también un montón de biblioteca) ya proveer de usted la manera de cifrar los datos sin tener que elegir cifrado de cifrado y de modo. El uso de ellos ! El bajo nivel de la API de Java tienen un montón de advertencias que son demasiado largas de explicar en los comentarios.
  • En realidad esa es la cosa más absurda que he leído en TAN! Quién eres tú para decirle a la gente lo que se puede y no se puede desarrollar?
  • Todavía veo ninguna ejemplos @HoLyVieR. Vamos a ver algunos, o punteros a las bibliotecas? No constructiva en todos.

InformationsquelleAutor TedTrippin | 2013-03-21

11 Kommentare

  1. 244

    Muchas personas incluyéndome a mí enfrentan a muchos problemas en la fabricación de este trabajo debido a la falta de cierta información como olvidar a convertir a Base64, la inicialización de los vectores, el conjunto de caracteres, etc. Así que pensé en hacer un totalmente funcional código.

    Esperanza de que será útil para todos vosotros:
    Para compilar necesita más de Apache Commons Codec frasco, 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")));
        }
    }
    • Si no quieres depender de la 3ª parte de Apache Commons biblioteca de Códec puede utilizar el JDK javax.xml.se unen.DatatypeConverter para realizar la codificación Base64/decodificación: System.out.println("encrypted string:" + DatatypeConverter.printBase64Binary(encrypted)); byte[] original = cipher.doFinal(DatatypeConverter.parseBase64Binary(encrypted));
    • Utilizando un constante IV?!
    • Java 8 ya ha Base64 herramientas: java.util.Base64.getDecoder() y java.util.Base64.getEncoder()
    • El IV no tiene que ser un secreto, pero tiene que ser imprevisible para el modo CBC (y único para CTR). Puede ser enviado junto con el texto cifrado. Una forma común de hacer esto es con el prefijo de la IV del texto cifrado y de cortar antes de descifrado. Debe ser generada a través de SecureRandom
    • En Android, en lugar de utilizar el Apache Commons Códec de biblioteca, puede utilizar android.util.Base64. El código que he usado es return Base64.encodeToString(encrypted, Base64.DEFAULT); y byte[] original = cipher.doFinal(Base64.decode(encrypted, Base64.DEFAULT));.
    • Como Hristo Stoyanov dicho, a la de Java 8 sustituir en cifrar método: return Base64.getEncoder().encodeToString(encrypted); y en descifrar método: byte[] original = cipher.doFinal(Base64.getDecoder().decode(encrypted));
    • Una contraseña no es fundamental. Una inyección INTRAVENOSA debe ser al azar.
    • Que no funcionó para mí. Lo que funcionó fue la sustitución de algunos de sus (@chandpriyankara) código con : final byte[] iv = new byte[16]; Matrices.relleno(iv, (byte) 0x00); IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); .. // el resto de los preparativos ecipher.init(sistema de Cifrado.ENCRYPT_MODE, skeySpec, ivParameterSpec);
    • Me permito sugerir que usted reemplace Arrays.fill(iv, (byte) 0x00) con new SecureRandom().nextBytes(iv)?
    • Si usted necesita Base16 cadena en lugar de Base64, el uso de javax.xml.se unen.DatatypeConverter del parseHexBinary() y printHexBinary() métodos.
    • ¿Por qué son los métodos de la estática? Vi ejemplos similares en otros lugares y en todos ellos el uso de métodos estáticos. Por qué?
    • La asignación de recursos para la función que sucede sólo una vez, no por cada instancia de objeto.
    • Pregunta rápida: ¿es un cifrado de 128 bits ya que la clave la clave es ?
    • es mejor el valor de uso.getBytes(StandardCharsets.UTF_8) al convertir un String Byte[] ; y la Cadena(original, StandardCharsets.UTF_8) cuando la conversión de una matriz de bytes en una Cadena para hacer la conversión de plataforma independiente

  2. 40

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

    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));

    Imprime:

    Hello world!
    դ;��LA+�ߙb*
    Hello world!
    • Este es un perfecto ejemplo funcionales, como @chandpriyankara del. Pero, ¿por qué definir una firma de encrypt(String) y no encrypt(byte[] ) ?. El cifrado descifrado demasiado) es un byte proceso de base (AES es de todos modos). El cifrado se lleva bytes de entrada y salidas de bytes, por lo que no descifrado (caso en cuestión : el Cipher objeto). Ahora, un caso de uso en particular puede ser que ha cifrado bytes procedentes de una Cadena, o ser enviado como una Cadena (base64 MIME archivo adjunto de un Correo electrónico…), sino que es un problema de codificación de bytes, para lo cual existe cientos de soluciones, totalmente ajenas a los AES/cifrado.
    • Sí, pero me parece más útil con Strings ya que es básicamente lo que yo trabajo con el 95% del tiempo y se termina la conversión de todos modos.
    • No, esto no es equivalente a chandpriyankara del código! Utiliza el código BCE, que generalmente es insegura y no quería. Debe especificar explícitamente CBC. Cuando CBC es especificado, el código se va a romper.
    • Perfectamente funcional, totalmente inseguro, y con muy malas prácticas de programación. la clase se llama mal. El tamaño de la clave no está marcado de antemano. Pero lo que es más importante, el código utiliza la inseguridad en modo ECB, ocultar el problema en la pregunta original. Por último, no especifica la codificación de caracteres, lo que significa que la decodificación de texto puede fallar en otras plataformas.
  3. 23

    A mí me parece que usted no está trabajando correctamente con el Vector de Inicialización (IV).
    Ha sido un largo tiempo desde la última vez que lea acerca de AES, IVs y encadenamiento de bloques, pero su línea de

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

    no parecen estar bien. En el caso de AES, se puede pensar en el vector de inicialización como el «estado inicial» de un sistema de cifrado de instancia, y este estado es un poco de información que no se puede obtener a partir de su clave, pero de la real cómputo del cifrado de cifrado. (Uno podría argumentar que si el IV podría ser extraído de la clave, entonces sería de ninguna utilidad, como la clave que ya está dado para el sistema de cifrado de instancia durante su init fase).

    Por lo tanto, usted debe obtener el IV como un byte[] en el sistema de cifrado de ejemplo, al final de su cifrado

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

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

      IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

    Entonces, su descifrado se debe ACEPTAR.
    Espero que esto ayude.

    • Gracias por ayudar a un novato. Yo empedradas este ejemplo de otros puestos. Supongo que usted no sabe cómo evitar la necesidad de una inyección INTRAVENOSA? He visto, pero no lo he probado, otras AES ejemplos que no lo uso.
    • Ignorar que, he encontrado la respuesta! Necesito usar AES/BCE/PKCS5Padding.
    • Más de veces que no quiero usar BCE. Google acaba de por qué.
    • Hola, estoy tratando de utilizar esto como es (espero que no la mente) con las correcciones, encontrar un ejemplo de cifrado/descifrado en Java que podía conectarlo a mi código parece francamente imposible. Lo que no puedo entender es, ¿de dónde viene la frase de paso de entrar? Usted se parece a generar la clave de la nada, con ninguna entrada del usuario. ¿Qué tipo de cifrado es que, si el usuario no tiene que proporcionar ningún tipo de clave secreta con el fin de descifrar los datos? No tiene ningún sentido para mí.
    • esta es una cuestión diferente por completo. La generación y almacenamiento de claves/IVs es un problema difícil ™. Usted puede buscar en stackoverflow.com/questions/2037021/… . Pero básicamente, tienes razón que en este ejemplo, se generan las claves del aire. Otra forma podría ser la de pedir al usuario que introduzca una frase de paso y utilizar como entrada de un algoritmo de generación de claves… Mira en «contraseña de claves basada en la derivación de» un.k.una. PBKDF2 como una palabra clave. Ver la aceptación de la solución en stackoverflow.com/questions/992019/…
    • Creo que la aceptó responder método de configuración de la IV es el mejor aunque se podría establecer a partir de la clave como se ha demostrado.
    • de acuerdo en que la elección y explícitamente la configuración de un IV, de confianza, de origen aleatorio, es mejor que dejar que el Cihper instancia a recoger uno. Por otro lado, esta respuesta presenta el problema original de confundir el vector de inicialización para la clave. Es por esto que es upvoted en primera. Ahora, este post se ha convertido en más de un código de ejemplo ir-a punto, y la gente de aquí hecho algunos grandes exemple – justo al lado de lo que la pregunta original era de unos.
    • Upvoted. El otro «gran ejemplo» no son tan grandes, y que en realidad no abordar la cuestión en absoluto. En cambio, este parece haber sido el lugar para los novatos copiar ciegamente criptográficos muestras sin el entendimiento de que no puede ser posible que los problemas de seguridad y, como siempre, hay.

  4. 17

    El IV que su uso para el 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 que debería resolver su problema.


    A continuación se incluye un ejemplo de un simple AES clase en Java. Yo no recomiendo el uso de esta clase en los entornos de producción, como es posible que no cuenta para 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)));
        }
    }

    Nota que AES no tiene nada que ver con la codificación, que es la razón por la que elegí para manejar por separado y sin la necesidad de las bibliotecas de terceros.

    • En primer lugar, usted no ha contestado a la pregunta original. Segundo, por qué responder a una contestado ya, bien aceptado pregunta? Pensé que la protección se supone que parar esto de spam.
    • Como el aceptado contestar, me eligió para responder a su pregunta a través del ejemplo. Yo siempre totalmente funcional pieza de código que muestra cómo tratar adecuadamente con el vector de inicialización, entre otras cosas. En cuanto a tu segunda pregunta, sentí que una respuesta actualizada que se necesitaba como el Apache codec no es necesario. Así que no, esto no es spam. Dejar de trippin.
    • Un IV tiene un propósito específico que es el de aleatorizar el texto cifrado y proporcionar semántica de seguridad. Si utiliza la misma clave+IV par, a continuación, los atacantes pueden determinar si usted ha enviado un mensaje con el mismo prefijo, como antes. El IV no tiene que ser un secreto, pero tiene que ser impredecible. Una forma común es simplemente el prefijo de la IV del texto cifrado y cortar antes de descifrado.
    • downvote: codificado IV, ver Artjom B. comentario sobre por qué es mala
    • CTR modo debe ser emparejado con NoPadding. CTR modo ciertamente no es necesario en lugar de CBC (a menos que el relleno de los oráculos de pago), pero si el CTR de la se se utiliza, a continuación, utilizar "/NoPadding". El CTR es un modo que se convierte AES de cifrado de flujo, y un sistema de cifrado de flujo opera en bytes en lugar de bloques.
  5. 15

    En esta respuesta que elegir el enfoque de la «Simple Java AES cifrar/descifrar ejemplo,» el tema principal y no el específico de depuración pregunta porque creo que este beneficio para la mayoría de los lectores.

    Este es un simple resumen de mi entrada de blog acerca de cifrado AES en Java así que recomiendo la lectura a través de él antes de implementar cualquier cosa. Yo, sin embargo, todavía proporcionar un ejemplo sencillo de usar y dar algunas indicaciones, lo que mirar hacia fuera para.

    En este ejemplo voy a elegir el uso de cifrado autenticado con Galois/Modo de Contador o GCM modo. La razón es que en la mayoría de los caso de que quieras la integridad y la autenticidad en combinación con confidencialidad (leer más en la blog).

    AES-GCM de Cifrado/Descifrado Tutorial

    Aquí están los pasos necesarios para cifrar/descifrar con AES-GCM con el Java Cryptography Architecture (JCA). No mezclar con otros ejemplos, como sutiles diferencias pueden hacer que tu código sea totalmente inseguro.

    1. Crear Clave

    Ya que depende de su caso de uso, voy a suponer el caso más simple: una clave secreta aleatoria.

    SecureRandom secureRandom = new SecureRandom();
    byte[] key = new byte[16];
    secureRandom.nextBytes(key);
    SecretKey secretKey = SecretKeySpec(key, "AES");

    Importante:

    2. Crear el Vector de Inicialización

    Un vector de inicialización (IV) se utiliza para que la misma clave secreta se crean diferentes sistema de cifrado de textos.

    byte[] IV = new byte[12]; //NEVER REUSE THIS IV WITH SAME KEY
    secureRandom.nextBytes(IV);

    Importante:

    3. Cifrar con IV y Clave

    final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    GCMParameterSpec parameterSpec = new GCMParameterSpec(128, IV); //128 bit auth tag length
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
    byte[] cipherText = cipher.doFinal(plainText);

    Importante:

    • uso de 16 bytes /128 bits la autenticación de etiqueta (utilizado para verificar la integridad/autenticidad)
    • la autenticación de etiqueta se agrega automáticamente al texto cifrado (en la JCA de la aplicación)
    • desde GCM se comporta como un sistema de cifrado de flujo, sin relleno
    • uso CipherInputStream cuando el cifrado de grandes bloques de datos
    • quiere adicionales (no secreto) de datos comprueba si se ha cambiado? Puede que desee utilizar datos asociados con cipher.updateAAD(associatedData); Más aquí.

    3. Serializar a Solo Mensaje

    Sólo anexar IV y el texto cifrado. Como se indicó anteriormente, el IV no debe ser secreto.

    ByteBuffer byteBuffer = ByteBuffer.allocate(4 + IV.length + cipherText.length);
    byteBuffer.putInt(IV.length);
    byteBuffer.put(IV);
    byteBuffer.put(cipherText);
    byte[] cipherMessage = byteBuffer.array();

    Opcionalmente codificar con Base64 si usted necesita una representación de cadena. Utilizar Android o Java 8 incorporado en aplicación (no usar Apache Commons Codec – es una terrible implementación). La codificación se utiliza para «convertir» matrices de bytes a la representación de cadena para hacer ASCII seguro por ejemplo:

    String base64CipherMessage = Base64.getEncoder().encodeToString(cipherMessage);

    4. Preparar Descifrado: Deserializar

    Si han codificado el mensaje, primero decodificación de matriz de bytes:

    byte[] cipherMessage = Base64.getDecoder().decode(base64CipherMessage)

    a continuación, desmontar el mensaje

    ByteBuffer byteBuffer = ByteBuffer.wrap(cipherMessage);
    int ivLength = byteBuffer.getInt();
    if(ivLength < 12 || ivLength >= 16) { //check input parameter
        throw new IllegalArgumentException("invalid IV length");
    }
    byte[] IV = new byte[ivLength];
    byteBuffer.get(IV);
    byte[] cipherText = new byte[byteBuffer.remaining()];
    byteBuffer.get(cipherText);

    Importante:

    5. Descifrar

    Inicializar el sistema de cifrado y establecer los mismos parámetros que con el cifrado:

    final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, IV));
    byte[] plainText= cipher.doFinal(cipherText);

    Importante:

    • no te olvides de añadir datos asociados con cipher.updateAAD(associatedData); si que ha añadido que durante el cifrado.

    Nota de que la más reciente de Android (SDK 21+) y Java (7+) de las implementaciones deben tener AES-GCM. Las versiones más antiguas carecen de ella. Yo todavía seleccione este modo, ya que es más fácil de implementar, además de ser más eficiente en comparación a modo similar de Cifrar-entonces-Mac (con, por ejemplo,AES-CBC + HMAC). Ver este artículo sobre cómo implementar AES-CBC con HMAC.

    • El problema es que pidiendo ejemplos es explícitamente fuera de tema en TAN. Y el mayor problema es que estos no revisadas son piezas de código que son difíciles de validar. Agradezco el esfuerzo, pero no creo que ASÍ debería ser el lugar para esto.
    • Admiro el esfuerzo, aunque, así que sólo voy a señalar un solo error: «el iv debe ser imprevisible, en combinación con el ser único (es decir. el uso de azar iv)» – esto es cierto para el modo CBC, pero no para los GCM.
    • this is true for CBC mode but not for GCM ¿te refieres a la parte entera, o sólo es, en realidad, no necesitan ser impredecible?
    • but I don't think that SO should be the place for this. usted está probablemente en lo cierto, pero parece que la mayoría va a seguir con ESO. Tal vez la mayoría de los usuarios no va a poner en el tiempo necesario para entender completamente el tema, pero tal vez una pareja va a ser muescas en la dirección correcta – ¿cómo crees que los principiantes guías debe ser publicado? El hecho es, que, por ejemplo, en Java/JCE la arquitectura es realmente difícil de entender, especialmente para alguien que no viene de la criptografía de estudios – y casi no hay buenos?
    • No siendo impredecible. El 12 de valor de byte es un nonce, y que sólo debe ser único (o más bien, la clave / valor nonce combinación debe ser único). Si usted no recibe el tema, a continuación, usted probablemente no debería utilizar primitivas de bajo nivel en el primer lugar (que ahora son a menudo llamados «materiales peligrosos» para los protocolos de nivel superior). Estoy de acuerdo pesar de que es difícil encontrar buenas implementaciones del protocolo a utilizar.
    • «Si usted no recibe el tema, a continuación, usted probablemente no debería utilizar primitivas de bajo nivel en el primer lugar» seguro, que DEBE ser el caso, muchos de los desarrolladores todavía lo hago. No estoy seguro de que abstenerse de colocar el contenido de alta calidad en cuanto a la seguridad/de la criptografía en los lugares donde a menudo no hay mucho es la solución correcta para esto. – thx para apuntar a mi error, por cierto
    • OK, solo porque me gusta la respuesta de w.r.t. contenido (en lugar de propósito): el IV de manejo puede ser simplificado, especialmente durante el descifrado: Java hace que sea fácil crear una inyección INTRAVENOSA directamente a partir de una matriz de bytes, después de todo. Lo mismo va para el descifrado, que no tiene que iniciar en el offset 0. Toda esta copia es simplemente no es necesario. También si tienes que enviar una longitud de la IV (¿?) entonces, ¿por qué no utilizar una sola (sin signo) no de byte – no vas a pasar de 255 bytes para el IV, derecho?

  6. 2

    Editor en línea Ejecutable de la versión:-

    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")));
        }
    }
    • perfecto, funciona perfectamente bien para mí
    • Fresco, feliz ayudó!
    • Una contraseña es no una clave, un IV no debe ser estática. Todavía stringly escrito el código, lo que hace imposible para destruir la clave. No hay ninguna indicación de qué hacer con el IV, ni cualquier noción de que debe ser impredecibles.
  7. 2

    Esta es una mejora con respecto a la aceptación de la respuesta.

    Cambios:

    (1) el Uso de azar IV y escriba el texto cifrado

    (2) el Uso de SHA-256 para generar una clave a partir de una frase de contraseña

    (3) No dependencia de Apache Commons

    public static void main(String[] args) throws GeneralSecurityException {
        String plaintext = "Hello world";
        String passphrase = "My passphrase";
        String encrypted = encrypt(passphrase, plaintext);
        String decrypted = decrypt(passphrase, encrypted);
        System.out.println(encrypted);
        System.out.println(decrypted);
    }
    
    private static SecretKeySpec getKeySpec(String passphrase) throws NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        return new SecretKeySpec(digest.digest(passphrase.getBytes(UTF_8)), "AES");
    }
    
    private static Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
        return Cipher.getInstance("AES/CBC/PKCS5PADDING");
    }
    
    public static String encrypt(String passphrase, String value) throws GeneralSecurityException {
        byte[] initVector = new byte[16];
        SecureRandom.getInstanceStrong().nextBytes(initVector);
        Cipher cipher = getCipher();
        cipher.init(Cipher.ENCRYPT_MODE, getKeySpec(passphrase), new IvParameterSpec(initVector));
        byte[] encrypted = cipher.doFinal(value.getBytes());
        return DatatypeConverter.printBase64Binary(initVector) +
                DatatypeConverter.printBase64Binary(encrypted);
    }
    
    public static String decrypt(String passphrase, String encrypted) throws GeneralSecurityException {
        byte[] initVector = DatatypeConverter.parseBase64Binary(encrypted.substring(0, 24));
        Cipher cipher = getCipher();
        cipher.init(Cipher.DECRYPT_MODE, getKeySpec(passphrase), new IvParameterSpec(initVector));
        byte[] original = cipher.doFinal(DatatypeConverter.parseBase64Binary(encrypted.substring(24)));
        return new String(original);
    }
    • Un hash no es todavía una contraseña basada en función de generación de claves / PBKDF. Ya sea que usted use un estudio aleatorizado clave o utilizar una PBKDF como PBKDF2 / Contraseña de Cifrado Basado en.
    • Puedes sugerir una mejora?
    • PBKDF2 está presente en Java, por lo que creo que acabo de sugerir una. OK, no código uno, pero eso es pedir un poco demasiado en mi opinión. Hay un montón de ejemplos de Contraseña de Cifrado Basado en.
    • Pensé que podría ser una solución sencilla. Por curiosidad, ¿qué sería de vulnerabilidades específicas al utilizar este código como está?
  8. 1

    Es a menudo la buena idea confiar en la biblioteca estándar proporciona la solución:

    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 a codificar».

    Solución se basa en Java Cryptography Architecture Guía De Referencia y https://stackoverflow.com/a/20591539/146745 respuesta.

    • Nunca utilice el modo de ECB. Período.
    • El BCE no debe ser utilizado si el cifrado de más de un bloque de datos con la misma clave, por lo que para el «Texto a codificar» es lo suficientemente bueno. stackoverflow.com/a/1220869/146745
    • la clave se genera en preparar sección clave: aesKey = keygen.generateKey()
  9. 1

    Otra solución usando java.util.Base64 con Spring Boot

    Encriptador De Clase

    package com.jmendoza.springboot.crypto.cipher;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;
    import java.nio.charset.StandardCharsets;
    import java.util.Base64;
    
    @Component
    public class Encryptor {
    
        @Value("${security.encryptor.key}")
        private byte[] key;
        @Value("${security.encryptor.algorithm}")
        private String algorithm;
    
        public String encrypt(String plainText) throws Exception {
            SecretKeySpec secretKey = new SecretKeySpec(key, algorithm);
            Cipher cipher = Cipher.getInstance(algorithm);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            return new String(Base64.getEncoder().encode(cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8))));
        }
    
        public String decrypt(String cipherText) throws Exception {
            SecretKeySpec secretKey = new SecretKeySpec(key, algorithm);
            Cipher cipher = Cipher.getInstance(algorithm);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)));
        }
    }

    EncryptorController Clase

    package com.jmendoza.springboot.crypto.controller;
    
    import com.jmendoza.springboot.crypto.cipher.Encryptor;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/cipher")
    public class EncryptorController {
    
        @Autowired
        Encryptor encryptor;
    
        @GetMapping(value = "encrypt/{value}")
        public String encrypt(@PathVariable("value") final String value) throws Exception {
            return encryptor.encrypt(value);
        }
    
        @GetMapping(value = "decrypt/{value}")
        public String decrypt(@PathVariable("value") final String value) throws Exception {
            return encryptor.decrypt(value);
        }
    }

    aplicación.propiedades

    server.port=8082
    security.encryptor.algorithm=AES
    security.encryptor.key=M8jFt46dfJMaiJA0

    Ejemplo

    http://localhost:8082/cipher/encrypt/jmendoza

    2h41HH8Shzc4BRU3hVDOXA==

    http://localhost:8082/cipher/decrypt/2h41HH8Shzc4BRU3hVDOXA==

    jmendoza

    Github: https://github.com/JonathanM2ndoza/Spring-Boot-Crypto

  10. 0

    Finalmente encriptación en java es tan simple de usar con Java Simplificado de Cifrado (Jasypt) , en otra manera que usted puede utilizar cypher, de arriba es un ejemplo de código para cifrar y descifrar :

    import java.security.spec.KeySpec;
    import java.util.Base64;
    
    import javax.crypto.Cipher;
    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 CypherUtils {
    
        private static final String AES_CBC_PKCS5_PADDING = "AES/CBC/PKCS5Padding";
        private static final String PBKDF2_WITH_HMAC_SHA256 = "PBKDF2WithHmacSHA256";
    
        public static String decrypt(String strToDecrypt, String keyCode, String salt) {
            try {
                byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                IvParameterSpec ivspec = new IvParameterSpec(iv);
    
                SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF2_WITH_HMAC_SHA256);
                KeySpec spec = new PBEKeySpec(keyCode.toCharArray(), salt.getBytes(), 65536, 256);
                SecretKey tmp = factory.generateSecret(spec);
                SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
    
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
                cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
                return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
            } catch (Exception e) {
                System.out.println("Error while decrypting: " + e.toString());
            }
            return null;
        }
    
        public static String encrypt(String strToEncrypt, String keyCode, String salt) {
            try {
                byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                IvParameterSpec ivspec = new IvParameterSpec(iv);
    
                SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF2_WITH_HMAC_SHA256);
                KeySpec spec = new PBEKeySpec(keyCode.toCharArray(), salt.getBytes(), 65536, 256);
                SecretKey tmp = factory.generateSecret(spec);
                SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
    
                Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5_PADDING);
                cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
                return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
            } catch (Exception e) {
                System.out.println("Error while encrypting: " + e.toString());
            }
            return null;
        }
    }
  11. 0

    Versión optimizada de la aceptó respuesta.

    • no la 3ª parte de libs

    • IV incluye en el mensaje cifrado (puede ser público)

    • contraseña puede ser de cualquier longitud

    Código:

    import java.io.UnsupportedEncodingException;
    import java.security.SecureRandom;
    import java.util.Base64;
    
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    
    public class Encryptor {
        public static byte[] getRandomInitialVector() {
            try {
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
                SecureRandom randomSecureRandom = SecureRandom.getInstance("SHA1PRNG");
                byte[] initVector = new byte[cipher.getBlockSize()];
                randomSecureRandom.nextBytes(initVector);
                return initVector;
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return null;
        }
    
        public static byte[] passwordTo16BitKey(String password) {
            try {
                byte[] srcBytes = password.getBytes("UTF-8");
                byte[] dstBytes = new byte[16];
    
                if (srcBytes.length == 16) {
                    return srcBytes;
                }
    
                if (srcBytes.length < 16) {
                    for (int i = 0; i < dstBytes.length; i++) {
                        dstBytes[i] = (byte) ((srcBytes[i % srcBytes.length]) * (srcBytes[(i + 1) % srcBytes.length]));
                    }
                } else if (srcBytes.length > 16) {
                    for (int i = 0; i < srcBytes.length; i++) {
                        dstBytes[i % dstBytes.length] += srcBytes[i];
                    }
                }
    
                return dstBytes;
            } catch (UnsupportedEncodingException ex) {
                ex.printStackTrace();
            }
    
            return null;
        }
    
        public static String encrypt(String key, String value) {
            return encrypt(passwordTo16BitKey(key), value);
        }
    
        public static String encrypt(byte[] key, String value) {
            try {
                byte[] initVector = Encryptor.getRandomInitialVector();
                IvParameterSpec iv = new IvParameterSpec(initVector);
                SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
    
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
                cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
    
                byte[] encrypted = cipher.doFinal(value.getBytes());
                return Base64.getEncoder().encodeToString(encrypted) + " " + Base64.getEncoder().encodeToString(initVector);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
    
            return null;
        }
    
        public static String decrypt(String key, String encrypted) {
            return decrypt(passwordTo16BitKey(key), encrypted);
        }
    
        public static String decrypt(byte[] key, String encrypted) {
            try {
                String[] encryptedParts = encrypted.split(" ");
                byte[] initVector = Base64.getDecoder().decode(encryptedParts[1]);
                if (initVector.length != 16) {
                    return null;
                }
    
                IvParameterSpec iv = new IvParameterSpec(initVector);
                SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
    
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
                cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
    
                byte[] original = cipher.doFinal(Base64.getDecoder().decode(encryptedParts[0]));
    
                return new String(original);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
    
            return null;
        }
    }

    Uso:

    String key = "Password of any length.";
    String encrypted = Encryptor.encrypt(key, "Hello World");
    String decrypted = Encryptor.decrypt(key, encrypted);
    System.out.println(encrypted);
    System.out.println(decrypted);

    Ejemplo de salida:

    QngBg+Qc5+F8HQsksgfyXg== yDfYiIHTqOOjc0HRNdr1Ng==
    Hello World

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea