2009-05-15 26 views
7

Estoy tratando de descifrar un archivo en Java que fue encriptado en C# usando Rijndael/CBC/PKCS7. Sigo recibiendo la siguiente excepción:BadPaddingException: pad block corrupto

javax.crypto.BadPaddingException: bloque de almohadilla dañado
en org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal (origen desconocido)
en javax.crypto.Cipher.doFinal (DashoA13 * ..)
en AESFileDecrypter.decrypt (AESFileDecrypter.java:57)

cuando el método doFinal(inpbytes) es llamado por el servidor web para el primer byte []. Supongo que esto es un problema con la llave o IV. Tengo los archivos encriptados en mi sistema de archivos para probarlos. ¿Hay algo que alguien pueda ver claramente incorrecto con mi código a continuación?

*** keyStr es codificado en base64

public AESFileDecrypter(String keyStr){ 
    try { 
      Security.addProvider(new BouncyCastleProvider()); 
      convertIvParameter(); 
      key = new sun.misc.BASE64Decoder().decodeBuffer(keyStr); 

      //use the passed in Base64 decoded key to create a key object 
      decryptKey = new SecretKeySpec(key, "AES"); 

      //specify the encryption algorithm 
      decryptCipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); 

      //make a parameter object for the initialization vector(IV)    
      IvParameterSpec ivs = new IvParameterSpec(_defaultIv); 

      //initialize the decrypter to the correct mode, key used and IV 
      decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey, ivs);  
     } 
    catch (Exception e) { 
      e.printStackTrace(); 
    } 
} 

public void convertIvParameter() { 

    int[] iv = new int[] {11, 190, 165, 33, 68, 88, 11, 200, 245, 35, 68, 23, 60, 24, 223, 67}; 

    _defaultIv = new byte[16]; 

    for(int x = 0; x < _defaultIv.length; x++) { 
     _defaultIv[x] = (byte)iv[x]; 
    } 
} 

public void decryptUpdate(byte[] inpBytes) throws Exception { 
    //decrypt the byte passed in from the web server 
    decryptCipher.update(inpBytes); 
} 

public byte[] decryptFinal() throws Exception { 
    //decrypt the byte passed in from the web server 
    return decryptCipher.doFinal(); 
} 

//sends bytes to the client for diaply 
private void sendBytes(FileInputStream fis, OutputStream os)throws Exception { 
    //set the buffer size to send 4k segments of data 
aesFileDecrypter = new AESFileDecrypter(<Insert Key string here>); 

    byte[] buffer = new byte[4096]; 
    int bytes = 0, totalBytes = fis.available(); 

    //while there is still data to be sent keep looping and write the data 
    //to the output stream as the buffer is filled 
    try { 
     while ((bytes = fis.read(buffer)) != -1) { 
      aesFileDecrypter.decryptUpdate(buffer); 
      //os.write(buffer, 0, bytes); 
     } 

     os.write(aesFileDecrypter.decryptFinal(), 0, totalBytes); 
    } 
    catch(Exception e) { 
     e.printStackTrace(); 
    } 
} 

Respuesta

1

Por lo que yo sé AES se basa en Rijndael, pero la especificación no es exactamente lo mismo. Sugiero que compruebe la clave y el tamaño del bloque que está utilizando para cifrar en C# y los tamaños que se usan en Java. (.Net differences between Rijndael and AES).

+1

Gracias por la respuesta. El AES es para 256 bits, y el cifrado en C# usa 4k fragmentos de datos. En thurn estoy usando ese mismo tamaño cada vez que llamo doFinal() –

6

En primer lugar, para que quede claro, a partir de los comentarios a continuación, no debe llamar a doFinal() en cada bloque, porque doFinal() espera cualquier relleno al final, que evidentemente no estará allí en bloques intermedios. O bien (a) llame a update() sobre datos intermedios, luego doFinal() al final, o (b) simplemente organice para tener todos sus datos en un búfer o conjunto de bytes, y llame doFinal() una vez en todo el lote de trabajo.

No está claro en el código que publicó que eso es realmente lo que está haciendo, pero debe mencionarse por las dudas.

De no ser así, entonces, como un primer paso para la depuración, sugeriría que cualquiera de estos dos es más fácil para usted:

  • Descifrado en modo ECB sin relleno y ver lo que hay. Mire el primer bloque de datos que esto trae de vuelta. Si puede XOR esto con sus IV bytes y obtener los datos descifrados esperados, sabrá que su clave está OK.
  • Descarga de los bytes de clave reales de C# antes de la codificación de base 64 y Java después de decodificar y comprobar que son los mismos.

Como recuerdo, C# tiene bytes sin firmar (mientras que Java se firmó) por lo que hay algunos lugares donde hay espacio para que las cosas sutilmente vayan mal con byte signedness.

+0

Otro comentario que me dijeron es que un problema importante es llamar a "doFinal()" en cada bloque (solo debería hacerse una vez). Se sugirió que cambie el doFinal para actualizar y solo ejecute doFinal al final. O use secuencias de cifrado –

+0

Ah sí, lo siento, obviamente no leí su código con cuidado. Si llama a doFinal() por cada bloque, eso está mal. –

+0

Gracias Niel ... Agregué el código actualizado. Intenté llamar al método de actualización ahora desde el método sendBytes (que reside en el servidor web). y después de recorrer el flujo de entrada, llame al método doFinal(). Todavía obtiene la misma excepción en el método doFinal(), por lo que realiza un bucle en la secuencia de archivos que realiza la actualización() en cada byte []. ¿Alguna sugerencia? –

1

El doFinal() fue la anulación del código anterior, y terminé simplemente usando secuencias de cifrado en lugar del enfoque de actualización/doFinal. De esta forma podría usar FileInputStream y mi cifrado como parámetros para CipherInputStream, y luego pasar el resultado al navegador web a través de OutputStream. Al dividir la actualización y doFinal en sus propias llamadas a métodos, la tarea se hizo mucho más difícil y ambos métodos se eliminaron de la clase de desencriptador (dejando un solo ciclo while que se lee en fragmentos de datos y se lo envía al navegador). El proveedor de Bouncy Castle tampoco se necesitó en este caso y PKCS5Padding fue suficiente, lo cual fue otorgado por SunJCE.

3

He encontrado este problema antes.

Cuando escribí algo de código para hacer el cifrado y descifrado de esta manera:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
    cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sec, "AES"),new IvParameterSpec(new byte[cipher.getBlockSize()])); 
    byte[] encode = cipher.doFinal(data); 

    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sec, "AES"), new IvParameterSpec(new byte[cipher.getBlockSize()])); 
    byte[] decode = cipher.doFinal(encode); 

me olvidó la primera IvParameterSpec(new byte[cipher.getBlockSize()]) en el cifrado de datos, entonces me dieron una excepción "bloque de almohadilla dañado", por lo que tal vez usted debe comprobar que código de cifrado