¿Generando una clave AES segura?

3

Le pregunté a esta pregunta hace un tiempo sobre las IV en AES, y Recibí una respuesta muy agradable y útil (¡gracias!), Así que pensé que quizás ustedes podrían ayudarme otra vez, esta vez con la generación clave real.

TL; DR - Ver la parte inferior

Actualmente, estoy encriptando así:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecureRandom randomSecure = SecureRandom.getInstance("SHA1PRNG");
byte[] iv = new byte[cipher.getBlockSize()];
randomSecure.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParams);
rawCryptotext = cipher.doFinal(textToEncrypt.getBytes());

Como pueden ver, estoy usando la clase SecureRandom para generar el IV aleatorio.

Sin embargo :

Observe la variable key en la sexta línea de código:

  

SecretKeySpec skeySpec = new SecretKeySpec ( key .getBytes ("UTF-8"), "AES");

Esa variable proviene de mi método getKey() , que es el siguiente:

public static String getKey()
    {
        String possibleKey = "init";
        String validKey = null;

        while (possibleKey.length() != 16)
        {
            System.out.printf("\nPlease enter a 128-bit key: ");
            possibleKey = input.nextLine();
        }

        if (possibleKey.length() == 16)
        {
            validKey = possibleKey;
        }
        else
        {
            System.out.println("Something has gone very wrong...");
        }

        return validKey;
    }

Notará que recibí la clave del usuario y la pasé en texto sin formato a SecretKeySpec .

Entonces, mi pregunta: ¿Combinar una clave de texto sin formato con un IV aleatorio hace que el cifrado sea seguro? ¿O debería hacer un hash de la cadena de clave con SHA-256 o algo primero, y usar el resultado para la clave?

    
pregunta Android Dev 28.08.2016 - 21:26
fuente

2 respuestas

7

No

Lo que estás haciendo es pedirle al usuario que ingrese una cadena de 16 caracteres o, en otras palabras, una contraseña. Es cierto que 16 bytes pueden caber 128 bits, pero eso solo se aplica a los datos donde todos los valores posibles de bytes son (igualmente) posibles. El texto que consta de caracteres es todo menos eso. Primero, porque el conjunto de caracteres imprimibles es mucho más pequeño que el de todos los bytes. (Es probable que ni siquiera puedan ingresar todos los bytes posibles si lo intentaron). Segundo, porque los usuarios no ingresarán bytes aleatorios, sino palabras. Y las palabras son aún más predecibles.

Si desea que el usuario ingrese una contraseña, no limite la longitud (excepto quizás para establecer un mínimo), y use algo como PBKDF2 para estirar la clave y ralentizar las adivinaciones del diccionario contra la contraseña. (Como comentó @marstato).

Por otra parte, si desea generar una clave aleatoria, extraiga los bytes de un generador de bits aleatorios fuerte y guárdelo en un archivo.

    
respondido por el ilkkachu 28.08.2016 - 22:29
fuente
3

Supongo que key es una contraseña que el usuario ingresó.

No use eso para el cifrado directamente. Los ataques de fuerza bruta contra contraseñas de usuario (posiblemente muy débiles) son mucho más fáciles que contra una clave completa de 128 (o 192, 256) bits. El solo troceo de la contraseña para obtener una clave no funciona, ya que no ralentiza la fuerza bruta en una cantidad relevante. Las contraseñas deben estar trilladas decenas de miles de veces para retrasar un solo intento lo suficiente como para hacer que un ataque de fuerza bruta contra la contraseña no sea viable.

PBKDF2 es la solución para esto. Combina una sal, una función de hash segura y toneladas de iteraciones:

// obtain password from wherever you like
char[] password;

// generate salt using a secureRandom; store salt with ciphertext
byte[] salt;

// key size in bits
int keySize = 128;
// choose so that it takes the computer of your average user about 1 second to do the derivation (lower if running on mobile devices, higher on an enterprise server)
int nIterations = 10000;

SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBDKF2WithHmacSHA256");
SecretKeySpec keySpec = keyFactory.generateSecret(new PBEKeySpec(password, salt, keysize));
// init cipher with keySpec
    
respondido por el marstato 28.08.2016 - 22:29
fuente

Lea otras preguntas en las etiquetas