2016-11-03 73 views
0

我得到鑑於最終塊未正確填充錯誤,同時解密大型加密文件上的AES/CBC/PKCS5Padding密碼。解密AES/CBC/PKCS5Padding錯誤:鑑於最終塊未正確填充

我覺得這個問題是由於在cipher.init()方法中加入了錯誤的初始化向量造成的。

我無法在運行時讀取整個文件,所以我需要加密固定大小的塊。在這一點上,我正在創建IV並將其存儲到.txt文件。但在解密方法中,我在每個解密週期使用相同的IV。我應該如何改變這一點?

加密:

void encrypt() throws Exception{ 
    char[] password = passwordText.getText().toCharArray(); 
    byte[] salt = new byte[8]; 

    /* Creating and saving salt */ 
    salt = saveSalt(salt); 

    /* Securing password */ 
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); 
    KeySpec spec = new PBEKeySpec(password, salt, 65536, 128); 
    SecretKey tmp = factory.generateSecret(spec); 
    SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); 

    if (choosedFile != null) { 
     /* Choosing algorithm for decryption */ 
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 

     /* Getting plain file */ 
     CipherInputStream fis = new CipherInputStream(new FileInputStream(choosedFile), cipher); 
     CipherOutputStream fos = new CipherOutputStream(new FileOutputStream(choosedFile+".encrypted"), cipher); 

     /* Encrypting and Measuring */ 
     long startTime = System.currentTimeMillis(); 
     cipher.init(Cipher.ENCRYPT_MODE, secret); 
     byte[] rawText = new byte[128]; 
     int count; 
     while((count = fis.read(rawText)) > 0) { 
      System.out.println(count); 
      byte[] encryptedText = cipher.doFinal(rawText); 
      fos.write(encryptedText, 0, count); 
     } 
     long stopTime = System.currentTimeMillis(); 

     fis.close(); 
     fos.close(); 

     /* Creating initialization vector and storing*/ 
     byte[] iVector = cipher.getIV(); 
     saveIVector(iVector); 

     text.setText(text.getText() + "File was encrypted in " + (stopTime - startTime) + "ms.\n"); 
    } 

} 

解密:

void decrypt() throws Exception { 
    /* Getting salt */ 
    byte[] salt = getSalt(); 
    /* Getting initialization vector */ 
    byte[] iVector = getIVector(); 
    /* Getting user password */ 
    char[] password = passwordText.getText().toCharArray(); 


    /* Securing password */ 
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); 
    KeySpec spec = new PBEKeySpec(password, salt, 65536, 128); 
    SecretKey tmp = factory.generateSecret(spec); 
    SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); 

    if (choosedFile != null) { 

     /* Choosing algorithm for decryption */ 
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     /* Getting ciphered file */ 


     CipherInputStream fis = new CipherInputStream(new FileInputStream(choosedFile), cipher); 
     CipherOutputStream fos = new CipherOutputStream(new FileOutputStream(choosedFile+".decrypted"), cipher); 

     /* Decrypting and Measuring */ 
     long startTime = System.currentTimeMillis(); 
     cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iVector)); 
     byte[] rawText = new byte[128]; 
     int count; 
     while((count = fis.read(rawText)) > 0) { 
       byte[] encryptedText = cipher.doFinal(rawText); 
       fos.write(encryptedText, 0, count); 
      } 

     long stopTime = System.currentTimeMillis(); 

     fis.close(); 
     fos.close(); 
+0

既然你指定PKCS#5加密和解密將錯誤確實是在一個錯誤由於參數不正確而導致解密(密鑰,iv,加密數據)。解密失敗,所以填充也是垃圾。重新處理問題只包括加密並以十六進制提供加密數據。使用小輸入使其變得簡單。提供[mcve]。由於該錯誤與加密相關,因此將所有其他代碼刪除。 – zaph

+0

@ zaph對不起,我忘了提及加密和解密算法都工作得很好,直到我不得不將大文件剪切成固定大小的塊。Ergo,我不認爲需要加密數據的例子。但是我試圖加密和解密小文件。它以相同的結果結束。儘管如此,你是對的。加密不是應該的,但我不知道爲什麼。 – Amphoru

回答

1

當使用CipherInputStreamCipherOutputStream時,這些流將處理所有對密碼的調用(這就是爲什麼您在初始化時將密碼傳遞給它的原因)。您只需要正確初始化它,並通過數據流對數據進行流式處理,並且密碼流將完成對update()doFinal()的所需調用。記得關閉蒸汽以觸發doFinal()

當前您的代碼以不受控制的方式多次通過密碼傳遞數據,並且數據混亂。

此外,您只需要解密的CipherInputStream和用於加密的CipherOutputStream。在您當前的代碼中,您同時使用加密和解密。

加密可能是這樣的(這不處理IV ..):

...  
cipher.init(Cipher.ENCRYPT_MODE, secret); 
InputStream is = new FileInputStream(choosedFile); 
OutputStream os = new CipherOutputStream(new FileOutputStream(choosedFile+".encrypted"), cipher); 

byte[] buffer = new byte[1024]; 
int len; 
while ((len = is.read(buffer)) != -1) { 
    os.write(buffer, 0, len); 
} 

is.close(); 
os.close(); 
... 
+0

我想我知道你想說什麼。但是你能否給我提供一些更好的例子?我不明白爲什麼我只需要在加密時使用'CipherOutputStream',當我需要輸入文件塊時,將其加密並將其傳遞給輸出。然後做同樣的事情,直到我加密了所有的文件塊。我在運行時沒有整個原始1GB數據。 – Amphoru

+0

我無法在運行時從1GB文件'readAllBytes()'。 – Amphoru

+0

@Amphoru我用一些加密代碼更新了我的答案。 –

1

擔心 「直到我不得不削減大文件分成固定大小的塊」。

使用「塊」代替上述「塊」,因爲「塊」在分組密碼中具有特定含義,如AES。

什麼是玩具與大塊,連接它們?

使用CBC模式,在第一個塊之後,先前的加密塊值被有效地用作下一個塊的IV。因此,當拆分並連接塊時,前一個塊的最後一個塊的值是下一個塊的IV。

請參閱CBC mode

或者你在做一些完全不同的事情嗎?

+0

正如你上面看到'byte [] rawText''表示這些文件塊。 (對於誤導性的名稱,應該是類似rawChunkOfFile的東西)我試圖加密這個塊並將其寫入輸出文件。比另一塊大塊做同樣的事情,直到沒有剩下的塊。但我認爲,我應該使用'cipher.update()'方法而不是'cipher.doFinal()'。 – Amphoru

+0

是使用'cipher.update()',它傳播塊鏈接。不要忘記調用'cipher.final()'。請參閱[CBC模式](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29)。 – zaph

+0

我應該使用'cipher.update()',然後寫塊輸出和'while()'後,我應該調用'cipher.final()'。還是應該在最後一個文件塊上調用final? – Amphoru

相關問題