Dado bloque final no debidamente acolchado


Estoy tratando de implementar un algoritmo de cifrado basado en contraseñas, pero obtengo esta excepción:

[2] Javax.criptografía.BadPaddingException: Dado el bloque final no correctamente acolchado

¿Cuál podría ser el problema? (Soy nuevo en Java.)

Aquí está mi código:

public class PasswordCrypter {

    private Key key;

    public PasswordCrypter(String password)  {
        try{
            KeyGenerator generator;
            generator = KeyGenerator.getInstance("DES");
            SecureRandom sec = new SecureRandom(password.getBytes());
            generator.init(sec);
            key = generator.generateKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public byte[] encrypt(byte[] array) throws CrypterException {
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch (Exception e) { 
            e.printStackTrace();
        }
        return null;
    }

    public byte[] decrypt(byte[] array) throws CrypterException{
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch(Exception e ){
            e.printStackTrace();
        }
        return null;
    }
}

(La prueba de JUnit)

public class PasswordCrypterTest {

    private static final byte[] MESSAGE = "Alpacas are awesome!".getBytes();
    private PasswordCrypter[] passwordCrypters;
    private byte[][] encryptedMessages;

    @Before
    public void setUp() {
        passwordCrypters = new PasswordCrypter[] {
            new PasswordCrypter("passwd"),
            new PasswordCrypter("passwd"),
            new PasswordCrypter("otherPasswd")
        };

        encryptedMessages = new byte[passwordCrypters.length][];
        for (int i = 0; i < passwordCrypters.length; i++) {
            encryptedMessages[i] = passwordCrypters[i].encrypt(MESSAGE);
        }
    }

    @Test
    public void testEncrypt() {
        for (byte[] encryptedMessage : encryptedMessages) {
            assertFalse(Arrays.equals(MESSAGE, encryptedMessage));
        }

        assertFalse(Arrays.equals(encryptedMessages[0], encryptedMessages[2]));
        assertFalse(Arrays.equals(encryptedMessages[1], encryptedMessages[2]));
    }

    @Test
    public void testDecrypt() {
        for (int i = 0; i < passwordCrypters.length; i++) {
            assertArrayEquals(MESSAGE, passwordCrypters[i].decrypt(encryptedMessages[i]));
        }

        assertArrayEquals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[1]));
        assertArrayEquals(MESSAGE, passwordCrypters[1].decrypt(encryptedMessages[0]));

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[2])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[2].decrypt(encryptedMessages[1])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }
    }
}
Author: aldok, 2011-11-08

2 answers

Si intenta descifrar los datos rellenados por PKCS5 con la clave incorrecta, y luego descomprimirlos (lo que se hace automáticamente por la clase de cifrado), lo más probable es que obtenga la excepción BadPaddingException (probablemente con un poco menos de 255/256, alrededor del 99.61%), porque el relleno tiene una estructura especial que se valida durante unpad y muy pocas claves producirían un relleno válido.

Por lo tanto, si obtiene esta excepción, atrápela y trátela como "clave incorrecta".

Esto también puede suceder cuando proporciona una contraseña incorrecta, que luego se utiliza para obtener la clave de un almacén de claves, o que se convierte en una clave mediante una función de generación de claves.

Por supuesto, el relleno incorrecto también puede ocurrir si sus datos están dañados en el transporte.

Dicho esto, hay algunas observaciones de seguridad sobre su esquema:

  • Para el cifrado basado en contraseñas, debe usar un SecretKeyFactory y PBEKeySpec en lugar de usar un SecureRandom con KeyGenerator. La razón es que el SecureRandom podría ser un algoritmo diferente en cada implementación de Java, dándole una clave diferente. SecretKeyFactory hace la derivación de claves de una manera definida (y de una manera que se considera segura, si selecciona el algoritmo correcto).

  • No utilice ECB-mode. Encripta cada bloque de forma independiente, lo que significa que los bloques de texto plano idénticos también dan bloques de texto cifrado siempre idénticos.

    Preferiblemente use un modo de operación seguro , como CBC (Cipher encadenamiento de bloques) o CTR (Contador). Alternativamente, use un modo que también incluya autenticación, como GCM (Galois-Counter mode) o CCM (Counter with CBC-MAC), consulte el siguiente punto.

  • Normalmente no solo desea confidencialidad, sino también autenticación, lo que garantiza que el mensaje no se manipule. (Esto también evita ataques de texto cifrado elegido en su cifrado, es decir, ayuda a la confidencialidad.) Por lo tanto, agregue un MAC (código de autenticación de mensajes) a su mensaje, o use un modo de cifrado que incluye la autenticación (ver punto anterior).

  • DES tiene un tamaño de clave efectivo de solo 56 bits. Este espacio clave es bastante pequeño, puede ser forzado brutalmente en algunas horas por un atacante dedicado. Si generas tu clave mediante una contraseña, esto será aún más rápido. Además, DES tiene un tamaño de bloque de solo 64 bits, lo que agrega algunas debilidades más en los modos de encadenamiento. Utilice un algoritmo moderno como AES en su lugar, que tiene un tamaño de bloque de 128 bits, y un tamaño de clave de 128 bits (para el estándar variante).

 166
Author: Paŭlo Ebermann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-23 17:23:11

Dependiendo del algoritmo de criptografía que esté utilizando, es posible que tenga que agregar algunos bytes de relleno al final antes de cifrar una matriz de bytes para que la longitud de la matriz de bytes sea múltiple del tamaño del bloque:

Específicamente en su caso, el esquema de relleno que eligió es PKCS5, que se describe aquí: http://www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_CJ_SYM__PAD.html

(Asumo que tienes el problema cuando intentas cifrar)

Puede elegir su esquema de relleno cuando instancie el objeto cifrado. Los valores admitidos dependen del proveedor de seguridad que esté utilizando.

Por cierto, ¿está seguro de que desea utilizar un mecanismo de cifrado simétrico para cifrar las contraseñas? ¿No sería un hachís de un solo sentido mejor? Si realmente necesita poder descifrar contraseñas, DES es una solución bastante débil, puede estar interesado en usar algo más fuerte como AES si necesita permanecer con un algoritmo simétrico.

 0
Author: fpacifici,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-09 22:00:08