2011-11-08 7 views
76

Estoy tratando de implementar el algoritmo de cifrado basado en contraseña, pero me da esta excepción:bloque final dado no rellena correctamente

javax.crypto.BadPaddingException: dado el bloque final no rellena correctamente

Lo 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 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. 
     } 
    } 
} 

Respuesta

152

Si intenta descifrar los datos acolchados PKCS5 con la clave incorrecta, y luego eliminarlos (lo que hace la clase Cipher automáticamente), lo más probable es que obtenga BadPaddingException (con probablemente algo menos de 255/256 , alrededor del 99.61%), debido a que el relleno tiene una estructura especial que se valida durante la descarga y muy pocas teclas producirán un relleno válido.

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

Esto también puede ocurrir 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 la función de generación de claves.

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

Dicho esto, hay algunas consideraciones de seguridad acerca de su esquema:

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

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

    Preferiblemente utilice un seguro mode of operation, como CBC (encadenamiento de bloques de cifrado) o CTR (contador). Alternativamente, use un modo que también incluya autenticación, como GCM (modo Galois-Counter) o CCM (Counter con CBC-MAC), consulte el siguiente punto.

  • Normalmente no solo desea confidencialidad, sino también autenticación, lo que garantiza que el mensaje no se altere. (Esto también evita los ataques de texto cifrado elegidos en su cifrado, es decir, ayuda a mantener la confidencialidad). Por lo tanto, agregue un MAC (código de autenticación de mensaje) a su mensaje o utilice un modo de cifrado que incluya autenticación (consulte el punto anterior).

  • DES tiene un tamaño efectivo de clave de solo 56 bits. Este espacio clave es bastante pequeño, puede ser forzado en algunas horas por un atacante dedicado. Si genera su clave con 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, que tiene un tamaño de bloque de 128 bits y un tamaño de clave de 128 bits (para la variante estándar).

+6

Agradable, respuesta completa – demongolem

+1

+1 Gran respuesta. –

+1

Solo quiero confirmarlo. Soy nuevo en el cifrado y este es mi caso, estoy usando el cifrado AES. en mi función de cifrado/descifrado, estoy usando una clave de cifrado. Utilicé una clave de cifrado incorrecta en el descifrado y obtuve esta 'javax.crypto.BadPaddingException: dado que el bloque final no está adecuadamente ajustado '. ¿Debería tratar esto como una clave incorrecta? – kenicky

1

dependiendo del algoritmo de criptografía que está utilizando, es posible que tenga que añadir 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 de bloque:

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

(que se supone que tiene el problema cuando intenta cifrar)

Se puede elegir el esquema de relleno cuando se instancia el objeto de 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 mejor una forma de hash? Si realmente necesita poder descifrar contraseñas, DES es una solución bastante débil, puede que le interese usar algo más fuerte como AES si necesita permanecer con un algoritmo simétrico.

+1

Se me olvida mencionar que el problema está en el descifrado. – Altrim

+1

, ¿podría publicar el código que intenta encriptar/descifrar? (y compruebe que la matriz de bytes que intenta descifrar no es más grande que el tamaño del bloque) – fpacifici

+1

Soy muy nuevo en Java y también en criptografía, así que todavía no conozco mejores formas de hacer el cifrado. Solo quiero hacer esto listo que probablemente busque mejores formas de implementarlo. – Altrim

Cuestiones relacionadas